# AAVE <> Lido ## About The goal is to provide the ability to deposit stETH into AAVE and allow to use it as collateral and for variable rate borrowing. aToken uses underliyng stETH shares to store balances and implement rebase ability. Motivation behind this design is to encourage using stETH as collateral rather than borrowing it. stETH is pegged steadily to ETH, so using it as collateral involves low liquidation risks. ## Audits Significant changes are being proposed to tokens contracts, so Lido DAO will make sure both of them undergo in-depth review and security assessment by established audit providers. ### Scope Target branch: https://github.com/lidofinance/aave-protocol-v2/tree/feature/steth-on-prev-version Target commit: https://github.com/lidofinance/aave-protocol-v2/commit/12c9111990c9699e84a36f30ba849486ef8f2a84 ## Implementation The astETH externally behaves similar to every other aTokens. It always maintains a 1:1 peg with the underlying stETH. The following should always be true. 1. At any time, user can deposit x stETH to mint x astETH. * Total astETH supply increases by x. 1. At any time, user can burn x astETH for x stETH. * Total astETH supply decreases by x. 1. At any time, userA can transfer x astETH to userB. * userA's astETH balance reduces by x. userB's astETH balance increases by x. Total astETH supply exactly remains same. 1. When stETH's supply rebases, the part of astETH related to the unborrowed underlying stETH rebases as well. The amount of borrowed stETH stays unchanged after rebasing. 1. The totalSupply of astETH should always be equal to (total stETH held in the system) + (the amount of borrowed stETH) + (interest). \* <b>Note</b>: Actual amount of asset will be less or equal to *X* because of integer operations rounding of underlying token rebase rate and AAVE interest rate. However, actual rounding error will not exceed couple wei at any time. ### aToken Generic aTokens have a private balance and public balance: ``` # _balanceOf[u] and _totalSupply from contract storage. balanceOf(u) = _balanceOf[u] * I totalSupply() = _totalSupply * I where, · I is AAVE's interest rate factor ``` 1. The internal (fixed-supply) `_balanceOf` and `_totalSupply` are referred to as `scaledBalanceOf` and `scaledTotalSupply` and correspond to the deposited balance without accrued interest. 1. The external (elastic-supply) `balanceOf` and `totalSupply` correspond to the deposited balance with interest. ### astETH Rebaseable astETH token has an additional book-keeping layer on top of the existing aToken structure. Thus, it has 2 private balances and 1 public balance. 1. The internal (fixed-supply) `balanceOf` and `totalSupply` are referred as `_shares` and `_totalShares`, used for book-keeping. The deposited balance is stored in stETH shares as `_totalShares` and converted into the amount of the tokens with functions from stETH. Users' balances are book-kept with the underlying ERC20 token. 1. The internal (elastic-supply) `balanceOf` and `totalSupply` are referred to as `_balanceScaled` and `_scaledTotalSupply` and correspond to the deposited balance without accrued interest. Rebasing of astETH according to stETH acts at this layer. Rebasing effects only the unborrowed part of `_totalShares`. 1. The external (elastic-supply) `balanceOf` and `totalSupply` correspond to the deposited balance with interest. ``` heldShares = _totalShares - _borrowedShares heldStETH = stETH.getPooledEthByShares(heldShares) _scaledTotalSupply = heldStETH + _borrowedStETH totalSupply = _scaledTotalSupply * I where, · _totalShares - is amount of stETH shares in astETH pool · _borrowedShares - borrowed shares book-kept on the debtToken side · _borrowedStETH - borrowed stETH book-kept on the debtToken side ``` ``` scaleFactor = _scaledTotalSupply / _totalSupply _balanceScaled = _shares[u] * scaleFactor balance(u) = _balanceScaled * I where, · _shares[u] - is user balance from the underlying ERC20 token. · _totalSupply - is the total supply of the underlying ERC20 token. ``` ``` · I - is AAVE's interest rate factor · stETH.getPooledEthByShares() - is method of stETH to convert shares into stETH ``` Token is implemented in `AStETH.sol`. - `function _fetchExtData()`: get external data for the math of aToken from the debtToken and stETH. - `function _scaledBalanceOf(uint256, uint256, uint256)`: get `_balanceScaled` for the specific user. - `function _scaledTotalSupply(ExtData)`: get `_scaledTotalSupply`. #### Additional calculation for shares Amount of shares to operate with for `mint`, `mintToTreasury`, `burn` and `transfer` methods require do additional calculation w.r.t the generic implementation. ##### Mint and burn: Additional calculations perform through `_mintScaled` and `_burnScaled`. The internal amount of tokens to mint is calculated with: ``` internalBefore - pair of internal balance of user and total supply before minting(burning); scaledBefore - pair of scaled balance of user and total supply before minting(burning); amountScaled - specific amount of tokens to mint(burn) scaled by the AAVE's interest rate factor; amountInternal - target amount of tokens to mint(burn) for internal fixed-supply token; internalAfter = internalBefore +(-) amountInternal, elementwise operation scaledAfter = scaledBefore +(-) amountScaled, elementwise operation internalAfter.balanceOf / internalAfter.totalSupply = scaledAfter.balanceOf / scaledAfter.totalSupply , therefore a = internalBefore.totalSupply * scaledAfter.balanceOf b = scaledAfter.totalSupply * internalBefore.balanceOf otherBalance = scaledBefore.totalSupply - scaledBefore.balanceOf for mint: amountInternal = (a - b) / otherBalance for burn: amountInternal = (b - a) / otherBalance ``` Such ways of minting and burning help to perform fair operations according to previous borrowings and rebases. Implemented in: `function _mintScaled(address, uint256, ExtData)` `function _burnScaled(address, uint256, ExtData)` Also, every `mint`, `burn` and `mintToTreasury` should have additional operations for book-keeping of the current deposited shares, i.e. book-keeping in `function mint(address, uint256, uint256)`: ```solidity function mint(address user, uint256 amount, uint256 index) { ... _mintScaled(user, amountScaled, _fetchExtData()); // book-keeping new shares _totalShares = _totalShares.add( ISTETH(_underlyingAsset).getSharesByPooledEth(amountScaled).toInt256Safe() ); ... } ``` ##### Scaled transfer To transfer the correct amount of internal tokens for the specific amount of aTokens: ``` amountInternal = amountScaled * internalTotalSupply / scaledTotalSupply ``` Implemented in: `function _transferScaled(address, address, uint256, ExtData)` ### debtTokens #### VariableDebtToken The VariableDebtToken implementation make no functional changes to the generic implementation. When the debt tokens are mint and burnt, it performs additional operations to keep track of the amount of borrowed stETH in shares. This amount is stored as `_totalSharesBorrowed`. The new `getBorrowData` method returns the amount of borrowed shares and the total supply of the debtToken (is equal to the amount of borrowed stETH) which are used for astETH math. The debtToken is non-rebasable token; the debt value is equal to the borrow + interest even after the rebasing of stETH. > repay operation could leave both positive and negative balance for `_totalSharesBorrowed` according to the rebasings that have occurred. Token is implemented in `VariableDebtStETH.sol`. #### StableDebtToken The StableDebtToken is fully based on the generic implementation except the `mint` operation. With current implementation every call of `mint` is reverted to protect from unexpected using of the token. The reason for this behavior is the constraint of using a stable borrowing for the stETH reserve. In future, if stable borrowing will be needed, the implementation will be changed. Token is implemented in `StableDebtStETH.sol`. ## Borrowing strategy When listing the stETH, only the variableDebtToken will be deployed, and the ability to borrow stETH will be disabled. In the future, the loan opportunity may be restored for stimulating the market on top of borrowed stETH. ## Incentives Controller As `IncentivesController` address should be provided on deploy and couldn't be upgraded, proxies addresses will be provided for both tokens. Lido DAO agent would be owner of both proxies to provide ability to upgrade it via the Lido DAO voting. Initially, implementation of astETH `IncentivesController` mock with empty callback `handleAction(address, uint256, uint256)` will be deployed and will be upgraded separately when the model of incentivization will be choosen by Lido governance. > We realize that having IncentivesController owned externally may involve extra security risks for AAVE. Lido is open to other options, if there is a process to upgrade an AAVE owned contract in the future.