Skip to content

Commit

Permalink
contracts: migration simulation: Test migration simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
bingen committed Mar 22, 2021
1 parent adf854f commit 1a6bf7b
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 136 deletions.
137 changes: 137 additions & 0 deletions packages/contracts/tests/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from brownie import Wei

from helpers import *

ZERO_ADDRESS = '0x' + '0'.zfill(40)
MAX_BYTES_32 = '0x' + 'F' * 64
MAX_FEE = Wei(1e18)

def floatToWei(amount):
return Wei(amount * 1e18)
Expand Down Expand Up @@ -32,3 +35,137 @@ def logGlobalState(contracts):
#print('Last trove ', last_trove)
print('Last trove’s ICR', last_ICR.to("ether"))
print(' ----------------------\n')

def setAddresses(contracts):
contracts.sortedTroves.setParams(
MAX_BYTES_32,
contracts.troveManager.address,
contracts.borrowerOperations.address,
{ 'from': accounts[0] }
)

contracts.troveManager.setAddresses(
contracts.borrowerOperations.address,
contracts.activePool.address,
contracts.defaultPool.address,
contracts.stabilityPool.address,
contracts.gasPool.address,
contracts.collSurplusPool.address,
contracts.priceFeedTestnet.address,
contracts.lusdToken.address,
contracts.sortedTroves.address,
contracts.lqtyToken.address,
contracts.lqtyStaking.address,
{ 'from': accounts[0] }
)

contracts.borrowerOperations.setAddresses(
contracts.troveManager.address,
contracts.activePool.address,
contracts.defaultPool.address,
contracts.stabilityPool.address,
contracts.gasPool.address,
contracts.collSurplusPool.address,
contracts.priceFeedTestnet.address,
contracts.sortedTroves.address,
contracts.lusdToken.address,
contracts.lqtyStaking.address,
{ 'from': accounts[0] }
)

contracts.stabilityPool.setAddresses(
contracts.borrowerOperations.address,
contracts.troveManager.address,
contracts.activePool.address,
contracts.lusdToken.address,
contracts.sortedTroves.address,
contracts.priceFeedTestnet.address,
contracts.communityIssuance.address,
{ 'from': accounts[0] }
)

contracts.activePool.setAddresses(
contracts.borrowerOperations.address,
contracts.troveManager.address,
contracts.stabilityPool.address,
contracts.defaultPool.address,
{ 'from': accounts[0] }
)

contracts.defaultPool.setAddresses(
contracts.troveManager.address,
contracts.activePool.address,
{ 'from': accounts[0] }
)

contracts.collSurplusPool.setAddresses(
contracts.borrowerOperations.address,
contracts.troveManager.address,
contracts.activePool.address,
{ 'from': accounts[0] }
)

contracts.hintHelpers.setAddresses(
contracts.sortedTroves.address,
contracts.troveManager.address,
{ 'from': accounts[0] }
)

# LQTY
contracts.lqtyStaking.setAddresses(
contracts.lqtyToken.address,
contracts.lusdToken.address,
contracts.troveManager.address,
contracts.borrowerOperations.address,
contracts.activePool.address,
{ 'from': accounts[0] }
)

contracts.communityIssuance.setAddresses(
contracts.lqtyToken.address,
contracts.stabilityPool.address,
{ 'from': accounts[0] }
)

@pytest.fixture
def contracts():
contracts = Contracts()

contracts.priceFeedTestnet = PriceFeedTestnet.deploy({ 'from': accounts[0] })
contracts.sortedTroves = SortedTroves.deploy({ 'from': accounts[0] })
contracts.troveManager = TroveManager.deploy({ 'from': accounts[0] })
contracts.activePool = ActivePool.deploy({ 'from': accounts[0] })
contracts.stabilityPool = StabilityPool.deploy({ 'from': accounts[0] })
contracts.gasPool = GasPool.deploy({ 'from': accounts[0] })
contracts.defaultPool = DefaultPool.deploy({ 'from': accounts[0] })
contracts.collSurplusPool = CollSurplusPool.deploy({ 'from': accounts[0] })
contracts.borrowerOperations = BorrowerOperations.deploy({ 'from': accounts[0] })
contracts.hintHelpers = HintHelpers.deploy({ 'from': accounts[0] })
contracts.lusdToken = LUSDToken.deploy(
contracts.troveManager.address,
contracts.stabilityPool.address,
contracts.borrowerOperations.address,
{ 'from': accounts[0] }
)
# LQTY
contracts.lqtyStaking = LQTYStaking.deploy({ 'from': accounts[0] })
contracts.communityIssuance = CommunityIssuance.deploy({ 'from': accounts[0] })
contracts.lockupContractFactory = LockupContractFactory.deploy({ 'from': accounts[0] })
contracts.lqtyToken = LQTYToken.deploy(
contracts.communityIssuance.address,
contracts.lqtyStaking.address,
contracts.lockupContractFactory.address,
accounts[0], # bountyAddress
accounts[0], # lpRewardsAddress
{ 'from': accounts[0] }
)

setAddresses(contracts)

return contracts

@pytest.fixture
def add_accounts():
if network.show_active() != 'development':
print("Importing accounts...")
import_accounts(accounts)
71 changes: 71 additions & 0 deletions packages/contracts/tests/migration_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from brownie import Wei

import random

from helpers import *

ETHER_PRICE = Wei(1000 * 1e18)

def flesh_out_system(accounts, contracts):
MIN_NET_DEBT = contracts.troveManager.MIN_NET_DEBT()
MIN_LUSD_AMOUNT = contracts.troveManager.getBorrowingFeeWithDecay(MIN_NET_DEBT)

# Account 0 will always be the last one, as one has to remain in the system
collateral = MIN_LUSD_AMOUNT * 3 / ETHER_PRICE # ICR: 300%
contracts.borrowerOperations.openTrove(MAX_FEE, MIN_LUSD_AMOUNT, ZERO_ADDRESS, ZERO_ADDRESS,
{ 'from': accounts[0], 'value': collateral })

# send 1 lqty token to account 1 (deployer cannot stake during 1st year),
# so it can stake and earn borrowing fees
contracts.lqtyToken.transfer(accounts[1], 1, { 'from': accounts[0] })
contracts.lqtyStaking.stake(1, { 'from': accounts[1] })

# open trove in ICR descending order
ICR = 500
for i in range(1, 1000):
lusdAmount = MIN_LUSD_AMOUNT + floatToWei(1000000 * random.random()) # lusd ranging from min to 1M
ICR = ICR - 0.38 * random.random() # last trove ICR should be at ~ 500 - 999*0.38 = 120.38
coll = lusdAmount * floatToWei(ICR / 100) / Wei(1e18) / ETHER_PRICE
contracts.borrowerOperations.openTrove(MAX_FEE, lusdAmount, accounts[i-1], ZERO_ADDRESS,
{ 'from': accounts[i], 'value': coll })

def get_lusd_to_repay(accounts, contracts, account, debt):
if account == accouns[1]:
return
lusdBalance = contracts.lusdToken.balanceOf(account)

if debt > lusdBalance:
pending = debt - lusdBalance
sender_balance = contracts.lusdToken.balanceOf(accounts[1])
if sender_balance < pending:
print(f"\n ***Error: not enough LUSD to repay! {debt / 1e18} LUSD for {account}, pending {pending / 1e18}, sender balance {sender_balance / 1e18}")
return

contracts.lusdToken.transfer(account, pending, { 'from': accounts[1] })

def migrate_trove(accounts, contracts1, contracts2, i):
[debt, coll] = contracts.troveManager.getEntireDebtAndColl(account)[0]
get_lusd_to_repay(accounts, contracts1, accounts[i], debt)
# open trove in old system
contracts1.borrowerOperations.closeTrove({ 'from': accounts[i] })

# close trove in new system
contracts2.borrowerOperations.openTrove(MAX_FEE, debt, accounts[i-1], ZERO_ADDRESS,
{ 'from': accounts[i], 'value': coll })

def run_migration_desc(accounts, contracts1, contracts2):
for i in range(1, 1000):
migrate_trove(accounts, contracts1, contracts2, i)

# TODO: if in recovery mode, try redeeming instead of closing trove
def run_migration_asc(accounts, contracts1, contracts2):
for i in range(999, 0, -1):
migrate_trove(accounts, contracts1, contracts2, i)

# TODO: if in recovery mode, try redeeming instead of closing trove
def run_migration_rand(accounts, contracts1, contracts2):
remaining = list(range(1, 1000))
while(len(remaining) > 0):
i = random.randrange(0, len(remaining))
migrate_trove(accounts, contracts1, contracts2, i)
remaining.pop(i)
42 changes: 42 additions & 0 deletions packages/contracts/tests/migration_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pytest

from helpers import *
from simulation_helpers import *

def setup():
contracts1 = contracts()
flesh_out_system(contracts1)

logGlobalState(contracts1)

contracts2 = contracts()
logGlobalState(contracts2)

return [contracts1, contracts2]

def test_run_migration_desc(add_accounts):
[contracts1, contracts2] = setup

# migrate troves in ICR descending order
run_migration_desc()

logGlobalState(contracts1)
logGlobalState(contracts2)

def test_run_migration_asc(add_accounts):
[contracts1, contracts2] = setup

# migrate troves in ICR ascending order
run_migration_asc()

logGlobalState(contracts1)
logGlobalState(contracts2)

def test_run_migration_rand(add_accounts):
[contracts1, contracts2] = setup

# migrate troves in ICR random order
run_migration_rand()

logGlobalState(contracts1)
logGlobalState(contracts2)
1 change: 0 additions & 1 deletion packages/contracts/tests/simulation_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
NUM_LIQUIDATIONS = 10

MIN_NET_DEBT = 1950.0
MAX_FEE = Wei(1e18)

"""# Ether price (exogenous)
Expand Down

0 comments on commit 1a6bf7b

Please sign in to comment.