Specifications

Architectural Specifications

  1. Funds are stored within each specific aToken. This gives the protocol better segregation between assets, which favors the implementation of yield farming aware aTokens.

  2. Libraries reduces gas footprint of all the actions by 15 to 20% while optimising the code complexity and verbosity.

  3. All actions happen through LendingPool.

  4. Debt tokens track users debt.

Features Specifications

aTokens

  • Support of the EIP-2612

  • No interest rate redirection in the base aToken implementation

The following definitions are true:

  • LRasset t , the current liquidity rate. A function of the overall borrow rate and the utilisation rate LRt = RtUt

  • LIt, cumulated liquidity index. Interest cumulated by the reserve during the time interval ∆T , updated whenever a borrow, deposit, repay, redeem, swap, liquidation event occurs.

LIt = (LRt∆Tyear + 1)LIt−1 LI0 = 1 × 1027 = 1 ray

  • N It, reserve normalised income. Ongoing interest cumulated by the reserve N It = (LRt∆Tyear + 1)LIt−1

The user index factually disappears as a storage variable, and is stored together with the principal balance as a ratio that is called Scaled Balance, ScB. The balance of the user is calculated leading to increase or decrease on every action resulting in the mint or burn of aTokens:

  • Deposits. When a user deposits an amount m in the protocol, his scaled balance updates as follows:

ScBt(x) = ScBt−1(x) + m N It

  • Withdrawals. When a user withdraws an amount m in the protocol, his scaled balance updates as follows:

ScBt(x) = ScBt−1(x) − m N It At any point in time, the aToken balance of a user can be written as: aBt(x) = ScBt(x)N It


Debt Tokenization

The total supply of debt token including debt accrued per second is defined as follows:

dSt = ∑ i∈users ScBt(i) with ScBt(i) the amount borrowed by each user i

The total debt on an asset at time t is defined by:

Dasset t = SDt + V Dt with SD the stable debt token supply and V D the variable


Variable Debt

The following definitions are true:

  • V Iasset t , the cumulated variable borrow index. Interest cumulated by the variable borrows V B during a time ∆T , at variable rate V R, updated whenever a borrow, deposit, repay, redeem, swap, liquidation event occurs.

V It = (1 + V Rt Tyear )∆T V It−1

  • V I(x), user cumulated variable borrow index Variable borrow index of the specific user, stored when a user opens a variable borrow position.//

V I(x) = V It(x)

  • P B(x), user principal borrow balance Balance stored when a user opens a borrow position. In case of multiple borrows, the compounded interest is cumulated each time and it becomes the new principal borrow balance.

The variable debt token follow the scaled balance approach. The concept of a normalised variable (cumulated) debt:

V Nt = (1 + V Rt Tyear )∆T V It−1

With ScV Bt(x) the scaled balance of a user x at time t, m the transaction amount, V Nt the normalised variable debt:

  • Borrows. When a user x borrows an amount m from the protocol the scaled balance updates

ScV Bt(x) = ScV Bt−1(x) + m V Nt

  • Repays/Liquidations. When a user x repays or gets liquidated an amount m the scaled balance updates

ScV Bt(x) = ScV Bt−1(x) − m V Nt

At any point in time, the total variable debt balance of a user can be written as:

V D(x) = ScV B(x)Dt


Stable Debt

For the stable rate debt, the following is true:

SRasset t , overall stable rate

  • When a stable borrow of amount SBnew is issued at rate SRt: SRt = SDtSRt−1+SBnew SRt SDt+SBnew

  • When a user x repays stable borrow i for an amount SBi(x) at stable rate SRi(x):

SRt = { 0, if SDt − SB(x) = 0 SDtSRt−1−SB(x)SR(x) SDt−SBx , if SDt − SBx > 0

SRt is stored in the stable rate token for each specific currency. The stable debt token SD(x) balance for a user x is defined as follows:

SD(x) = SB(x)(1 + SRt Tyear) )∆T

The stable rate SR(x) is calculated per asset reserve across the i stable loans:

SR(x) = ∑ i SRi(x)SDi(x) SDi(x)


Collateral Trading

Thrupenny offers a way of swapping assets deposited, both used as collateral or not. This is achieved in the following way:

  • The flashLoan() function is called by the user, passing as parameters the address of a receiver contract, implementing the IFlashLoanReceiver interface, which is an untrusted contract that should be verified by the user in advance; a list of underlying assets to swap from, a list of amounts of those assets and one extra parameter containing the asset to swap to and the max slippage chosen by the user, both encoded.

  • The receiver contract will then use the funds received to swap them to the destination asset, depositing again on behalf of the user and withdrawing the user’s deposits in the Flashed-currencies in order to repay the Flash Loan.


Repay with Collateral

This feature allows for an user with one or multiple assets deposited as collateral in the protocol, to use them to repay partially or totally his debt/s positions. Same as with Swap Liquidity and, in general all the features based on Flash Loans, the repayment with collateral uses the flashLoan() function and a receiver contract, implementing the IFlashLoanReceiver interface. To this receiver is passed a list collateral assets to flash and use to swap and repay, a list of amounts of those assets, and, encoded, a list of the asset to repay debt, a list of debt amounts to repay, and a list with the borrow modes (stable or variable) for each debt asset. It’s important to note that, in this case, the receiver contract will expect to receive an exact amount of how much needs to be repaid, differently to Swap Liquidity, where the amount expected is the exact collateral to swap. The flow on this feature would be:

  1. An user has 100 TPY deposited in the protocol, and a debt of 200 USDC at variable rate. As he doesn’t have at the moment USDC funds available to repay his loan, he wants to swap part of his TPY collateral to USDC and use it for the repayment.

  2. The user calls the flashLoan() function passing as parameters: the address of the receiver contract which contains the logic to do the operation, the address of TPY, 7 as collateral amount to swap (estimated TPY needed to cover the 200 USDC debt), 200 as debt amount to repay and variable as borrow mode used.

  3. The receiver contract will swap the 7 TPY to USDC by using an exchange.

  4. The receiver contract will use the resultant USDC from the swap to repay on behalf of the user his USDC debt in the protocol.

  5. Once the debt is repaid, the receiver will transfer the amount of aTPY needed to return the flashed TPY from the user, will redeem it for TPY, and will approve the pool to pull those funds at the end of the Flash Loan transaction.

  6. If at the end there are any leftovers of the 7 TPY as result of the swap, these funds will be deposited back in the protocol on behalf of the user or sent directly to his wallet.


Credit Delegation

The borrow() function supports credit lines to different addresses, without the need for collateral, as long as the caller address as been granted the allowance.

This functionality is implemented through the function approveDelegation() on each debt token. Users will be able to draw up to their allowance for the specific debt mode (stable or variable). The borrow() function has an onBehalfOf parameter for the caller to specify the address used to draw credit.

The implementation of credit delegation required some trade-offs:

  • A delegator can delegate credit to multiple entities but a delegatee can only draw credit from a single delegator at once. One cannot aggregate the delegators debt in a single borrow()

  • A delegator can simultaneously delegate stable and variable credit to an entity, but a delegatee cannot draw variable and stable credit from a single borrow()

Last updated