protocol-v2 icon indicating copy to clipboard operation
protocol-v2 copied to clipboard

Custom aAMPLToken

Open ahnaguib opened this issue 4 years ago • 0 comments

An implementation of a custom aToken for AMPL. Includes tests, modifications for deployment scripts, and audit report.

Relevant AIP: https://github.com/aave/aip/pull/37

AMPL total supply and balances change once every 24 hours (at 2am UTC) based on market price. The supply change factor is the same for all holders. Because of that, lending and borrowing AMPL behaves differently from fixed supply tokens.

The desired behavior is for the borrow amount (loan) to stay the same after a rebase. The unborrowed tokens deposited in the Aave protocol still get exposed to rebase just like any other contract wallet. The aAMPL token is modified from generic AToken to achieve that behavior.

The easiest way to see the changes in the new tokens is to compare them to the original ones. Diff AAmplToken.sol, AmplStableDebtToken.sol and AmplVariableDebtToken.sol in contracts/protocol/tokenization/ampl with the corresponding generic contracts.

AAmplToken

The AMPL AToken functionally behaves similarly to every other aToken. It always maintains a 1:1 peg with the underlying AMPL. It should guarantee the following to always be true.

Example Scenarios

https://colab.research.google.com/drive/1a4zd7UL-U5Xrme9b0X5B39nJzjrFwfG1?usp=sharing

1) At any time, user can deposit x AMPLs to mint x aAMPLs. 
   Total aAMPL supply increases by exactly x.

2) At any time, user can burn x aAMPLs for x AMPLs.
   Total aAMPL supply decreases by exactly x.

3) At any time, userA can transfer x aAMPLs to userB.
   userA's aAMPL balance reduces by X.
   userB's aAMPL balance increases by X.
   Total aAMPL supply exactly remains same.

4) When AMPL's supply rebases, only the 'unborrowed' aAMPL should rebase.
  => Say there are 1000 aAMPL, and 200 AMPL is lent out. AMPL expands by 10%.
     The new aAMPL supply should be 1080 aAMPL.
  => Say there are 1000 aAMPL, and 200 AMPL is lent out. AMPL contracts by 10%.
     The new aAMPL supply should be 920 aAMPL.

5) When AMPL's supply rebases, only the part of the balance of a user proportional to  the available liquidity ('unborrowed') should rebase.
  => Say a user has deposited 1000 AMPL and receives 1000 aAMPL, and 
     20% of the total underlying AMPL is lent out. AMPL expands by 10%.
     The new aAMPL user balance should be 1080 aAMPL.
  => Say a user has deposited 1000 AMPL and receives 1000 aAMPL, and 
     20% of the total underlying AMPL is lent out. AMPL contracts by 10%.
     The new aAMPL supply should be 920 aAMPL.

Implementation

We implement a custom AToken, StableDebtToken and VariableDebtToken for AMPL. The StableDebtToken and VariableDebtToken implementations make no functional changes to the generic implementation. When the debt tokens are mint and burnt, it performs some additional book-keeping to keep track of the totalScaledAMPL (fixed AMPL denomination, ie Gons) borrowed at any-time. The new getAMPLBorrowData method returns the totalScaledAMPL and totalScaledSupply which are used for AAmplToken math.

ATokenMath

Generic aToken Math

Generic AToken has a private balance and public balance. The public balance returned by balanceOf is multiplied by an interest rate factor.

_balances[u] and _totalSupply hold the balance and total supply in storage.

AToken(u) = ATokenInternal(u) . I
AToken(supply) = ATokenInternal(supply) . I

ATokenInternal(u) = _balances[u]
ATokenInternal(supply) = _totalSupply

For example, a user deposits 100 DAI to get 100 aDAI and accrues 5 DAI interest.

ADaiInternal(user) = 100
ADai(user) = 100 * 1.05 = 105 

aAMPL math

The aAMPL works the same way but the AAmplInternal balance is multiplied by another factor, which accounts for the underlying AMPL rebasing and the amount of AMPL unborrowed.

AAmpl(u) = AAmplInternal(u) . I
AAmpl(supply) = AAmplInternal(supply) . I

# AMPL_SCALAR is inferred from the AMPL ERC-20 contract
AAmplInternal(supply) = (totalScaledAMPLDeposited - totalScaledAMPLBorrowed)/AMPL_SCALAR + totalAMPLBorrowed

aAMPL_Scalar() = AAmplInternal(supply) / _totalSupply
AAmplInternal(u) = aAMPL_Scalar() * _balances[u]

ahnaguib avatar Apr 01 '21 20:04 ahnaguib