def liquidate(accounts, loanToken, underlyingToken, set_demand_curve, collateralToken, sovryn, priceFeeds, rate, WRBTC, FeesEvents, SOV, chain): # set the demand curve to set interest rates set_demand_curve() lender = accounts[0] borrower = accounts[1] liquidator = accounts[2] loan_token_sent = 10e18 # lend to the pool, mint tokens if required, open a margin trade position loan_id = prepare_liquidation(lender, borrower, liquidator, loan_token_sent, loanToken, underlyingToken, collateralToken, sovryn, WRBTC) loan = sovryn.getLoan(loan_id).dict() # set the rates so we're able to liquidate if(underlyingToken == WRBTC): priceFeeds.setRates(underlyingToken.address, collateralToken.address, rate) value = loan_token_sent else: priceFeeds.setRates(collateralToken.address, underlyingToken.address, rate) value = 0 sov_borrower_initial_balance = SOV.balanceOf(borrower) chain.sleep(10*24*60*60) # time travel 10 days chain.mine(1) # liquidate tx_liquidation = sovryn.liquidate(loan_id, liquidator, loan_token_sent, {'from': liquidator, 'value' :value}) verify_liquidation_event(loan, tx_liquidation, lender, borrower, liquidator, loan_token_sent, loanToken, underlyingToken, set_demand_curve, collateralToken, sovryn, priceFeeds, rate) if(underlyingToken != WRBTC): verify_sov_reward_payment(tx_liquidation, FeesEvents, SOV, borrower, loan_id, sov_borrower_initial_balance, 1)
def close_complete_margin_trade_sov_reward_payment( sovryn, set_demand_curve, lend_to_pool, open_margin_trade_position, chain, return_token_is_collateral, FeesEvents, SOV): #prepare the test set_demand_curve() (receiver, _) = lend_to_pool() (loan_id, trader, loan_token_sent, leverage_amount) = open_margin_trade_position() chain.sleep(10 * 24 * 60 * 60) # time travel 10 days chain.mine(1) initial_loan = sovryn.getLoan(loan_id) #needs to be called by the trader with reverts("unauthorized"): sovryn.closeWithSwap(loan_id, trader, loan_token_sent, return_token_is_collateral, "") #complete closure means the whole collateral is swapped swap_amount = initial_loan['collateral'] sov_initial_balance = SOV.balanceOf(trader) tx = sovryn.closeWithSwap(loan_id, trader, swap_amount, return_token_is_collateral, "", {'from': trader}) verify_sov_reward_payment(tx, FeesEvents, SOV, trader, loan_id, sov_initial_balance, 2)
def margin_trading_sending_collateral_tokens_sov_reward_payment( trader, loanToken, collateralToken, collateralTokenSent, leverageAmount, value, chain, FeesEvents, SOV): sov_initial_balance = SOV.balanceOf(trader) tx = loanToken.marginTrade( "0", # loanId (0 for new loans) leverageAmount, # leverageAmount 0, # loanTokenSent collateralTokenSent, collateralToken.address, # collateralTokenAddress trader, # trader, b'', # loanDataBytes (only required with ether), { 'from': trader, 'value': value }) tx.info() chain.sleep(10 * 24 * 60 * 60) chain.mine(1) constants = shared.Constants() loan_id = constants.ZERO_32 # is zero because is a new loan verify_sov_reward_payment(tx, FeesEvents, SOV, trader, loan_id, sov_initial_balance, 1)
def margin_trading_sov_reward_payment(accounts, loanToken, underlyingToken, collateralToken, chain, SOV, FeesEvents): # preparation loan_token_sent = 1e18 underlyingToken.mint(loanToken.address, loan_token_sent * 3) trader = accounts[0] underlyingToken.mint(trader, loan_token_sent) underlyingToken.approve(loanToken.address, loan_token_sent) value = 0 # send the transaction leverage_amount = 2e18 collateral_sent = 0 sov_initial_balance = SOV.balanceOf(trader) tx = loanToken.marginTrade( "0", #loanId (0 for new loans) leverage_amount, # leverageAmount loan_token_sent, #loanTokenSent collateral_sent, # no collateral token sent collateralToken.address, #collateralTokenAddress trader, #trader, b'', #loanDataBytes (only required with ether) {'value': value}) tx.info() chain.sleep(10 * 24 * 60 * 60) chain.mine(1) constants = shared.Constants() loan_id = constants.ZERO_32 # is zero because is a new loan verify_sov_reward_payment(tx, FeesEvents, SOV, trader, loan_id, sov_initial_balance, 1)
def close_partial_margin_trade_sov_reward_payment( sovryn, set_demand_curve, lend_to_pool, open_margin_trade_position, chain, return_token_is_collateral, FeesEvents, SOV): #prepare the test set_demand_curve() (receiver, _) = lend_to_pool() (loan_id, trader, loan_token_sent, leverage_amount) = open_margin_trade_position() chain.sleep(10 * 24 * 60 * 60) # time travel 10 days chain.mine(1) initial_loan = sovryn.getLoan(loan_id) swap_amount = fixedint(initial_loan['collateral']).mul(80 * 10**18).div( 10**20).num sov_initial_balance = SOV.balanceOf(trader) tx = sovryn.closeWithSwap(loan_id, trader, swap_amount, return_token_is_collateral, "", {'from': trader}) verify_sov_reward_payment(tx, FeesEvents, SOV, trader, loan_id, sov_initial_balance, 2)
def test_rollover(accounts, chain, loanToken, set_demand_curve, sovryn, priceFeeds, SUSD, RBTC, BZRX, SOV, FeesEvents): borrower, loan, loan_id = setup_rollover_test(RBTC, SUSD, accounts, chain, loanToken, set_demand_curve, sovryn) lender_interest_data = sovryn.getLenderInterestData(loanToken.address, SUSD.address).dict() lender_pool_initial_balance = SUSD.balanceOf(loanToken.address) sov_borrower_initial_balance = SOV.balanceOf(borrower) tx_rollover = sovryn.rollover(loan_id, b'') lender_interest_after = sovryn.getLenderInterestData(loanToken.address, SUSD.address).dict() lending_fee_percent = sovryn.lendingFeePercent() interest_unpaid = lender_interest_data['interestUnPaid'] lending_fee = fixedint(interest_unpaid).mul(lending_fee_percent).div(1e20) interest_owed_now = fixedint(interest_unpaid).sub(lending_fee) assert(SUSD.balanceOf(loanToken.address) == fixedint(lender_pool_initial_balance).add(interest_owed_now)) assert(lender_interest_after['interestPaid'] == interest_unpaid) assert(lender_interest_after['interestUnPaid'] == 0) # Settles and pays borrowers based on fees generated by their interest payments if sovryn.protocolTokenHeld() != 0: verify_sov_reward_payment(tx_rollover, FeesEvents, SOV, borrower, loan_id, sov_borrower_initial_balance, 2) loan_after = sovryn.getLoan(loan_id).dict() assert(loan_after['endTimestamp'] >= loan['endTimestamp'] + 28*24*60*60) (trade_rate, precision) = priceFeeds.queryRate(RBTC.address, SUSD.address) trading_fee_percent = sovryn.tradingFeePercent() trading_fee = fixedint(interest_unpaid).mul(trading_fee_percent).div(1e20) loan_swap_event = tx_rollover.events['LoanSwap'] assert(loan_swap_event['loanId'] == loan_id) assert(loan_swap_event['sourceToken'] == RBTC.address) assert(loan_swap_event['destToken'] == SUSD.address) assert(loan_swap_event['borrower'] == borrower) #source buffer = 10000 in sovryn swap connector assert(fixedint(loan_swap_event['sourceAmount']).sub(interest_unpaid).add(trading_fee).mul(precision).div(trade_rate).num <= 10000) assert(loan_swap_event['destAmount'] >= interest_unpaid)
def test_borrow(accounts, loanToken, sovryn, set_demand_curve, lend_to_pool, SUSD, RBTC, FeesEvents, SOV): # prepare the test set_demand_curve() lend_to_pool() # determine borrowing parameter withdrawAmount = 10e18 #i want to borrow 10 USD # compute the required collateral. params: address loanToken, address collateralToken, uint256 newPrincipal,uint256 marginAmount, bool isTorqueLoan collateralTokenSent = sovryn.getRequiredCollateral(SUSD.address, RBTC.address, withdrawAmount, 50e18, True) print("collateral needed", collateralTokenSent) durationInSeconds = 60 * 60 * 24 * 10 #10 days # compute expected values for asserts interestRate = loanToken.nextBorrowInterestRate(withdrawAmount) #principal = withdrawAmount/(1 - interestRate/1e20 * durationInSeconds / 31536000) principal = fixedint(withdrawAmount).mul(1e18).div( fixedint(1e18).sub( fixedint(interestRate).mul(durationInSeconds).mul(10e18).div( 31536000).div(10e20))) borrowingFee = fixedint( sovryn.borrowingFeePercent()).mul(collateralTokenSent).div(1e20) expectedBalance = fixedint(SUSD.balanceOf(accounts[1])).add(withdrawAmount) #approve the transfer of the collateral RBTC.approve(loanToken.address, collateralTokenSent) borrower = accounts[0] sov_initial_balance = SOV.balanceOf(borrower) # borrow some funds tx = loanToken.borrow( "0", # bytes32 loanId withdrawAmount, # uint256 withdrawAmount durationInSeconds, # uint256 initialLoanDuration collateralTokenSent, # uint256 collateralTokenSent RBTC.address, # address collateralTokenAddress borrower, # address borrower accounts[1], # address receiver b'' # bytes memory loanDataBytes ) #assert the trade was processed as expected print(tx.info()) borrow_event = tx.events['Borrow'] assert (borrow_event['user'] == borrower) assert (borrow_event['lender'] == loanToken.address) assert (borrow_event['loanToken'] == SUSD.address) assert (borrow_event['collateralToken'] == RBTC.address) assert (borrow_event['newPrincipal'] == principal) assert (borrow_event['newCollateral'] == fixedint(collateralTokenSent).sub( borrowingFee)) assert (borrow_event['interestRate'] == interestRate) assert (borrow_event['interestDuration'] >= durationInSeconds - 1 and borrow_event['interestDuration'] <= durationInSeconds) assert (borrow_event['currentMargin'] >= 49e18) #assert the user received the borrowed amount assert (SUSD.balanceOf(accounts[1]) == expectedBalance) verify_sov_reward_payment(tx, FeesEvents, SOV, borrower, borrow_event['loanId'], sov_initial_balance, 1)
def internal_test_close_with_deposit(deposit_amount, RBTC, SUSD, borrower, chain, collateral, initial_loan, initial_loan_interest, loanToken, loan_id, priceFeeds, principal, receiver, sovryn, LoanClosingsEvents, FeesEvents, SOV): SUSD.mint(borrower, deposit_amount) SUSD.approve(sovryn.address, deposit_amount, {'from': borrower}) (rate, precision) = priceFeeds.queryRate(initial_loan['collateralToken'], initial_loan['loanToken']) sov_borrower_initial_balance = SOV.balanceOf(borrower) tx = sovryn.closeWithDeposit(loan_id, receiver, deposit_amount, {'from': borrower}) tx.info() loan_close_amount = principal if deposit_amount > principal else deposit_amount withdraw_amount = collateral if loan_close_amount == principal \ else fixedint(collateral).mul(loan_close_amount).div(principal).num end_collateral = collateral - withdraw_amount end_principal = 0 if loan_close_amount == principal else principal - loan_close_amount collateral_to_loan_rate = fixedint(rate).mul(10**18).div(precision).num collateral_to_loan_amount = fixedint(end_collateral).mul( collateral_to_loan_rate).div(10**18).num current_margin = fixedint(collateral_to_loan_amount - end_principal).mul(10 ** 20).div(end_principal) \ if (end_principal <= collateral_to_loan_amount and end_principal != 0) else 0 owed_per_day = initial_loan_interest['interestOwedPerDay'] end_timestamp = initial_loan['endTimestamp'] owed_per_day_refund = fixedint(owed_per_day).mul(loan_close_amount).div( principal).num # (loan end timestamp - block timestamp) * owedPerDayRefund / 24*60*60 interest_refund_to_borrower_1 = fixedint( end_timestamp - chain[-1].timestamp).mul(owed_per_day_refund).div( 24 * 60 * 60).num interest_refund_to_borrower = 0 if interest_refund_to_borrower_1 <= loan_close_amount \ else interest_refund_to_borrower_1 - loan_close_amount # Test CloseWithDeposit event parameters # When all the tests are run, the event is not recognized so we have to decode it manually close_event = tx.events['CloseWithDeposit'] if 'CloseWithDeposit' in tx.events \ else decode_log(tx, LoanClosingsEvents, 'CloseWithDeposit') assert (close_event['user'] == borrower) assert (close_event['lender'] == loanToken.address) assert (close_event['loanId'] == loan_id) assert (close_event['closer'] == borrower) assert (close_event['loanToken'] == initial_loan['loanToken']) assert (close_event['collateralToken'] == initial_loan['collateralToken']) assert (close_event['repayAmount'] == loan_close_amount) assert (close_event['collateralWithdrawAmount'] == withdraw_amount) assert (close_event['collateralToLoanRate'] == collateral_to_loan_rate) assert (close_event['currentMargin'] == current_margin) # Test refund collateral to receiver # Test refund interest to receiver assert (RBTC.balanceOf(receiver) == withdraw_amount) assert (SUSD.balanceOf(receiver) == interest_refund_to_borrower) # Test loan update end_loan = sovryn.getLoan(loan_id) new_principal = 0 if loan_close_amount == principal else fixedint( principal).sub(loan_close_amount).num assert (end_loan['principal'] == new_principal) if loan_close_amount == principal: last_block_timestamp = chain[-1]['timestamp'] assert (end_loan['endTimestamp'] <= last_block_timestamp) # Test returning principal to lender with deposit loan_close_amount_less_interest = loan_close_amount - interest_refund_to_borrower_1 \ if loan_close_amount >= interest_refund_to_borrower_1 \ else 0 transfer_to_lender = list( filter(lambda tx_event: tx_event['from'] == borrower, tx.events['Transfer'])) assert (len(transfer_to_lender) == 1) transfer_to_lender = transfer_to_lender[0] assert (transfer_to_lender['to'] == loanToken.address) assert (transfer_to_lender['value'] == loan_close_amount_less_interest) verify_sov_reward_payment(tx, FeesEvents, SOV, borrower, loan_id, sov_borrower_initial_balance, 1)