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)
Example #2
0
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)
Example #3
0
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)
Example #4
0
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)
Example #5
0
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)
Example #6
0
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)
Example #7
0
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)
Example #8
0
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)