Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion contracts/connectors/loantoken/LoanTokenLogicStandard.sol
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,17 @@ contract LoanTokenLogicStandard is AdvancedToken {
return returnData;
}
*/


/**
* borrows funds from the pool. The underlying loan token may not be used as collateral.
* @param loanId the ID of the loan, 0 for a new loan
* @param withdrawAmount the amount to be withdrawn (actually borrowed)
* @param initialLoanDuration the duration of the loan in seconds. if the loan is not paid back until then, it'll need to be rolled over
* @param collateralTokenSent the amount of collateral token sent (150% of the withdrawn amount worth in collateral tokenns)
* @param collateralTokenAddress the address of the tokenn to be used as collateral. cannot be the loan token address
* @param borrower the one paying for the collateral
* @param receiver the one receiving the withdrawn amount
* */
function borrow(
bytes32 loanId, // 0 if new loan
uint256 withdrawAmount,
Expand Down
204 changes: 202 additions & 2 deletions tests/test_LoanToken.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_loanAddress(loanToken, SUSD):
assert loanTokenAddress == SUSD.address

@pytest.fixture(scope="module", autouse=True)
def margin_pool_setup(accounts, RBTC, loanTokenSettings, loanToken, sovryn, SUSD):
def loan_pool_setup(accounts, RBTC, loanTokenSettings, loanToken, sovryn, SUSD):
constants = shared.Constants()
params = [];
setup1 = [
Expand All @@ -62,6 +62,10 @@ def margin_pool_setup(accounts, RBTC, loanTokenSettings, loanToken, sovryn, SUSD
tx = loanToken.updateSettings(loanTokenSettings.address, calldata)
assert('LoanParamsSetup' in tx.events)
assert('LoanParamsIdSetup' in tx.events)
calldata = loanTokenSettings.setupTorqueLoanParams.encode_input(params)
tx = loanToken.updateSettings(loanTokenSettings.address, calldata)
assert('LoanParamsSetup' in tx.events)
assert('LoanParamsIdSetup' in tx.events)
print(tx.info())
sovryn.setLoanPool(
[loanToken.address],
Expand Down Expand Up @@ -682,7 +686,9 @@ def test_liquidate(accounts, loanToken, SUSD, set_demand_curve, RBTC, sovryn, pr
assert(liquidate_event['currentMargin'] == current_margin)



def test_rollover(accounts, chain, loanToken, set_demand_curve, sovryn, priceFeeds, SUSD, RBTC, BZRX):

"""
Tests paid interests to lender
Test that loan attributes are updated
Expand Down Expand Up @@ -764,6 +770,199 @@ def test_rollover(accounts, chain, loanToken, set_demand_curve, sovryn, priceFee
assert(loan_swap_event['destAmount'] == interest_unpaid)


def test_borrow(accounts,loanToken,sovryn,set_demand_curve,lend_to_pool, SUSD, RBTC):
'''
borrows some funds, checks the event is correct (including principal and collateral -> interest check)
and that the receiver received the correct amount of tokens
'''

# 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)

# borrow some funds
tx = loanToken.borrow(
"0", # bytes32 loanId
withdrawAmount, # uint256 withdrawAmount
durationInSeconds, # uint256 initialLoanDuration
collateralTokenSent, # uint256 collateralTokenSent
RBTC.address, # address collateralTokenAddress
accounts[0], # 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'] == accounts[0])
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)

def test_borrow_0_collateral_should_fail(accounts,loanToken,sovryn,set_demand_curve,lend_to_pool, SUSD, RBTC):
# prepare the test
lend_to_pool()
set_demand_curve()

with reverts("8"):
loanToken.borrow(
"0", # bytes32 loanId
10 , # uint256 withdrawAmount
24*60*60, # uint256 initialLoanDuration
0, # uint256 collateralTokenSent
RBTC.address, # address collateralTokenAddress
accounts[0], # address borrower
accounts[1], # address receiver
b'' # bytes memory loanDataBytes
)

def test_borrow_0_withdraw_should_fail(accounts,loanToken,sovryn,set_demand_curve,lend_to_pool, SUSD, RBTC):
# prepare the test
lend_to_pool()
set_demand_curve()

with reverts("6"):
loanToken.borrow(
"0", # bytes32 loanId
0 , # uint256 withdrawAmount
24*60*60, # uint256 initialLoanDuration
10, # uint256 collateralTokenSent
RBTC.address, # address collateralTokenAddress
accounts[0], # address borrower
accounts[1], # address receiver
b'' # bytes memory loanDataBytes
)

def test_borrow_sending_value_with_tokens_should_fail(accounts,loanToken,sovryn,set_demand_curve,lend_to_pool, SUSD, RBTC):
# prepare the test
lend_to_pool()
set_demand_curve()

with reverts("7"):
loanToken.borrow(
"0", # bytes32 loanId
10 , # uint256 withdrawAmount
24*60*60, # uint256 initialLoanDuration
10, # uint256 collateralTokenSent
RBTC.address, # address collateralTokenAddress
accounts[0], # address borrower
accounts[1], # address receiver
b'' , # bytes memory loanDataBytes
{'value': 100}
)

def test_borrow_invalid_collateral_should_fail(accounts,loanToken,sovryn,set_demand_curve,lend_to_pool, SUSD, RBTC):
# prepare the test
lend_to_pool()
set_demand_curve()
constants = shared.Constants()

with reverts("9"):
loanToken.borrow(
"0", # bytes32 loanId
10 , # uint256 withdrawAmount
24*60*60, # uint256 initialLoanDuration
10, # uint256 collateralTokenSent
constants.ZERO_ADDRESS, # address collateralTokenAddress
accounts[0], # address borrower
accounts[1], # address receiver
b'' # bytes memory loanDataBytes
)

with reverts("10"):
loanToken.borrow(
"0", # bytes32 loanId
10 , # uint256 withdrawAmount
24*60*60, # uint256 initialLoanDuration
10, # uint256 collateralTokenSent
SUSD.address, # address collateralTokenAddress
accounts[0], # address borrower
accounts[1], # address receiver
b'' # bytes memory loanDataBytes
)


def test_borrow_no_interest_should_fail(accounts,loanToken,sovryn,set_demand_curve,lend_to_pool, SUSD, RBTC):
#no demand curve settings -> no interest set
# prepare the test
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)

#approve the transfer of the collateral
RBTC.approve(loanToken.address, collateralTokenSent)

with reverts("invalid interest"):
loanToken.borrow(
"0", # bytes32 loanId
withdrawAmount , # uint256 withdrawAmount
24*60*60, # uint256 initialLoanDuration
collateralTokenSent, # uint256 collateralTokenSent
RBTC.address, # address collateralTokenAddress
accounts[0], # address borrower
accounts[1], # address receiver
b'' # bytes memory loanDataBytes
)


def test_borrow_insufficient_collateral_should_fail(accounts,loanToken,sovryn,set_demand_curve,lend_to_pool, SUSD, RBTC):
# prepare the test
lend_to_pool()
set_demand_curve()

# 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)
collateralTokenSent /= 2
print("sending collateral",collateralTokenSent)

#approve the transfer of the collateral
RBTC.approve(loanToken.address, collateralTokenSent)

with reverts("collateral insufficient"):
loanToken.borrow(
"0", # bytes32 loanId
withdrawAmount , # uint256 withdrawAmount
24*60*60, # uint256 initialLoanDuration
collateralTokenSent, # uint256 collateralTokenSent
RBTC.address, # address collateralTokenAddress
accounts[0], # address borrower
accounts[1], # address receiver
b'' # bytes memory loanDataBytes
)



def test_deposit_collateral(sovryn,set_demand_curve,lend_to_pool,open_margin_trade_position, RBTC):
#prepare the test
set_demand_curve()
Expand Down Expand Up @@ -795,4 +994,5 @@ def test_deposit_collateral_0_value(sovryn,set_demand_curve,lend_to_pool,open_ma
with reverts("depositAmount is 0"):
sovryn.depositCollateral(loan_id, 0)

#note: deposit collateral tests for WETH still missing.
#note: deposit collateral tests for WETH still missing.