{"content":{"title":"Aave中的AToken","body":"# 1 AToken概览\r\nAave是一个借贷平台，AToken属于存款凭证，当用户存入资产时，Aave会给用户mint一定数量的AToken，下面是AToken的代码概览\r\n```solidity\r\ncontract AToken is VersionedInitializable, ScaledBalanceTokenBase, EIP712Base, IAToken\r\n```\r\nAToken继承了VersionedInitializable、ScaledBalanceTokenBase、EIP712Base和IAToken，下面我们分别来看下他们的实现\r\n\r\n# 2 源码解读\r\n## 2.1 VersionedInitializable\r\n```solidity\r\n// SPDX-License-Identifier: AGPL-3.0\r\npragma solidity 0.8.10;\r\n\r\n/**\r\n * @title VersionedInitializable\r\n * @author Aave, inspired by the OpenZeppelin Initializable contract\r\n * @notice Helper contract to implement initializer functions. To use it, replace\r\n * the constructor with a function that has the `initializer` modifier.\r\n * @dev WARNING: Unlike constructors, initializer functions must be manually\r\n * invoked. This applies both to deploying an Initializable contract, as well\r\n * as extending an Initializable contract via inheritance.\r\n * WARNING: When used with inheritance, manual care must be taken to not invoke\r\n * a parent initializer twice, or ensure that all initializers are idempotent,\r\n * because this is not dealt with automatically as with constructors.\r\n */\r\nabstract contract VersionedInitializable {\r\n  /**\r\n   * @dev Indicates that the contract has been initialized.\r\n   */\r\n  uint256 private lastInitializedRevision = 0;\r\n\r\n  /**\r\n   * @dev Indicates that the contract is in the process of being initialized.\r\n   */\r\n  bool private initializing;\r\n\r\n  /**\r\n   * @dev Modifier to use in the initializer function of a contract.\r\n   */\r\n  modifier initializer() {\r\n    uint256 revision = getRevision();\r\n    require(\r\n      initializing || isConstructor() || revision > lastInitializedRevision,\r\n      'Contract instance has already been initialized'\r\n    );\r\n\r\n    bool isTopLevelCall = !initializing;\r\n    if (isTopLevelCall) {\r\n      initializing = true;\r\n      lastInitializedRevision = revision;\r\n    }\r\n\r\n    _;\r\n\r\n    if (isTopLevelCall) {\r\n      initializing = false;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * @notice Returns the revision number of the contract\r\n   * @dev Needs to be defined in the inherited class as a constant.\r\n   * @return The revision number\r\n   */\r\n  function getRevision() internal pure virtual returns (uint256);\r\n\r\n  /**\r\n   * @notice Returns true if and only if the function is running in the constructor\r\n   * @return True if the function is running in the constructor\r\n   */\r\n  function isConstructor() private view returns (bool) {\r\n    // extcodesize checks the size of the code stored in an address, and\r\n    // address returns the current address. Since the code is still not\r\n    // deployed when running a constructor, any checks on its code size will\r\n    // yield zero, making it an effective way to detect if a contract is\r\n    // under construction or not.\r\n    uint256 cs;\r\n    //solium-disable-next-line\r\n    assembly {\r\n      cs := extcodesize(address())\r\n    }\r\n    return cs == 0;\r\n  }\r\n\r\n  // Reserved storage space to allow for layout changes in the future.\r\n  uint256[50] private ______gap;\r\n}\r\n```\r\n这个合约是用来辅助初始化的，修改自OpenZeppelin里的Initializable合约，引入了revision版本号，当版本号变大时，可以再次初始化。\r\n\r\n## 2.2 ScaledBalanceTokenBase\r\n```solidity\r\nabstract contract ScaledBalanceTokenBase is MintableIncentivizedERC20, IScaledBalanceToken\r\n```\r\n我们可以看到ScaledBalanceTokenBase继承了MintableIncentivizedERC20：\r\n```solidity\r\nabstract contract MintableIncentivizedERC20 is IncentivizedERC20\r\n```\r\nMintableIncentivizedERC20又继承了IncentivizedERC20，因此我们先来看下IncentivizedERC20的实现\r\n### 2.2.1 IncentivizedERC20\r\n```solidity\r\nabstract contract IncentivizedERC20 is Context, IERC20Detailed {\r\n  using WadRayMath for uint256;\r\n  using SafeCast for uint256;\r\n\r\n  /**\r\n   * @dev Only pool admin can call functions marked by this modifier.\r\n   */\r\n  modifier onlyPoolAdmin() {\r\n    IACLManager aclManager = IACLManager(_addressesProvider.getACLManager());\r\n    require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN);\r\n    _;\r\n  }\r\n\r\n  /**\r\n   * @dev Only pool can call functions marked by this modifier.\r\n   */\r\n  modifier onlyPool() {\r\n    require(_msgSender() == address(POOL), Errors.CALLER_MUST_BE_POOL);\r\n    _;\r\n  }\r\n\r\n  /**\r\n   * @dev UserState - additionalData is a flexible field.\r\n   * ATokens and VariableDebtTokens use this field store the index of the\r\n   * user's last supply/withdrawal/borrow/repayment. StableDebtTokens use\r\n   * this field to store the user's stable rate.\r\n   */\r\n  struct UserState {\r\n    uint128 balance;\r\n    uint128 additionalData;\r\n  }\r\n  // Map of users address and their state data (userAddress => userStateData)\r\n  mapping(address => UserState) internal _userState;\r\n\r\n  // Map of allowances (delegator => delegatee => allowanceAmount)\r\n  mapping(address => mapping(address => uint256)) private _allowances;\r\n\r\n  uint256 internal _totalSupply;\r\n  string private _name;\r\n  string private _symbol;\r\n  uint8 private _decimals;\r\n  IAaveIncentivesController internal _incentivesController;\r\n  IPoolAddressesProvider internal immutable _addressesProvider;\r\n  IPool public immutable POOL;\r\n\r\n  /**\r\n   * @dev Constructor.\r\n   * @param pool The reference to the main Pool contract\r\n   * @param name The name of the token\r\n   * @param symbol The symbol of the token\r\n   * @param decimals The number of decimals of the token\r\n   */\r\n  constructor(\r\n    IPool pool,\r\n    string memory name,\r\n    string memory symbol,\r\n    uint8 decimals\r\n  ) {\r\n    _addressesProvider = pool.ADDRESSES_PROVIDER();\r\n    _name = name;\r\n    _symbol = symbol;\r\n    _decimals = decimals;\r\n    POOL = pool;\r\n  }\r\n\r\n  /// @inheritdoc IERC20Detailed\r\n  function name() public view override returns (string memory) {\r\n    return _name;\r\n  }\r\n\r\n  /// @inheritdoc IERC20Detailed\r\n  function symbol() external view override returns (string memory) {\r\n    return _symbol;\r\n  }\r\n\r\n  /// @inheritdoc IERC20Detailed\r\n  function decimals() external view override returns (uint8) {\r\n    return _decimals;\r\n  }\r\n\r\n  /// @inheritdoc IERC20\r\n  function totalSupply() public view virtual override returns (uint256) {\r\n    return _totalSupply;\r\n  }\r\n\r\n  /// @inheritdoc IERC20\r\n  function balanceOf(address account) public view virtual override returns (uint256) {\r\n    return _userState[account].balance;\r\n  }\r\n\r\n  /**\r\n   * @notice Returns the address of the Incentives Controller contract\r\n   * @return The address of the Incentives Controller\r\n   */\r\n  function getIncentivesController() external view virtual returns (IAaveIncentivesController) {\r\n    return _incentivesController;\r\n  }\r\n\r\n  /**\r\n   * @notice Sets a new Incentives Controller\r\n   * @param controller the new Incentives controller\r\n   */\r\n  function setIncentivesController(IAaveIncentivesController controller) external onlyPoolAdmin {\r\n    _incentivesController = controller;\r\n  }\r\n\r\n  /// @inheritdoc IERC20\r\n  function transfer(address recipient, uint256 amount) external virtual override returns (bool) {\r\n    uint128 castAmount = amount.toUint128();\r\n    _transfer(_msgSender(), recipient, castAmount);\r\n    return true;\r\n  }\r\n\r\n  /// @inheritdoc IERC20\r\n  function allowance(address owner, address spender)\r\n    external\r\n    view\r\n    virtual\r\n    override\r\n    returns (uint256)\r\n  {\r\n    return _allowances[owner][spender];\r\n  }\r\n\r\n  /// @inheritdoc IERC20\r\n  function approve(address spender, uint256 amount) external virtual override returns (bool) {\r\n    _approve(_msgSender(), spender, amount);\r\n    return true;\r\n  }\r\n\r\n  /// @inheritdoc IERC20\r\n  function transferFrom(\r\n    address sender,\r\n    address recipient,\r\n    uint256 amount\r\n  ) external virtual override returns (bool) {\r\n    uint128 castAmount = amount.toUint128();\r\n    _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - castAmount);\r\n    _transfer(sender, recipient, castAmount);\r\n    return true;\r\n  }\r\n\r\n  /**\r\n   * @notice Increases the allowance of spender to spend _msgSender() tokens\r\n   * @param spender The user allowed to spend on behalf of _msgSender()\r\n   * @param addedValue The amount being added to the allowance\r\n   * @return `true`\r\n   */\r\n  function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool) {\r\n    _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\r\n    return true;\r\n  }\r\n\r\n  /**\r\n   * @notice Decreases the allowance of spender to spend _msgSender() tokens\r\n   * @param spender The user allowed to spend on behalf of _msgSender()\r\n   * @param subtractedValue The amount being subtracted to the allowance\r\n   * @return `true`\r\n   */\r\n  function decreaseAllowance(address spender, uint256 subtractedValue)\r\n    external\r\n    virtual\r\n    returns (bool)\r\n  {\r\n    _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);\r\n    return true;\r\n  }\r\n\r\n  /**\r\n   * @notice Transfers tokens between two users and apply incentives if defined.\r\n   * @param sender The source address\r\n   * @param recipient The destination address\r\n   * @param amount The amount getting transferred\r\n   */\r\n  function _transfer(\r\n    address sender,\r\n    address recipient,\r\n    uint128 amount\r\n  ) internal virtual {\r\n    uint128 oldSenderBalance = _userState[sender].balance;\r\n    _userState[sender].balance = oldSenderBalance - amount;\r\n    uint128 oldRecipientBalance = _userState[recipient].balance;\r\n    _userState[recipient].balance = oldRecipientBalance + amount;\r\n\r\n    IAaveIncentivesController incentivesControllerLocal = _incentivesController;\r\n    if (address(incentivesControllerLocal) != address(0)) {\r\n      uint256 currentTotalSupply = _totalSupply;\r\n      incentivesControllerLocal.handleAction(sender, currentTotalSupply, oldSenderBalance);\r\n      if (sender != recipient) {\r\n        incentivesControllerLocal.handleAction(recipient, currentTotalSupply, oldRecipientBalance);\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * @notice Approve `spender` to use `amount` of `owner`s balance\r\n   * @param owner The address owning the tokens\r\n   * @param spender The address approved for spending\r\n   * @param amount The amount of tokens to approve spending of\r\n   */\r\n  function _approve(\r\n    address owner,\r\n    address spender,\r\n    uint256 amount\r\n  ) internal virtual {\r\n    _allowances[owner][spender] = amount;\r\n    emit Approval(owner, spender, amount);\r\n  }\r\n\r\n  /**\r\n   * @notice Update the name of the token\r\n   * @param newName The new name for the token\r\n   */\r\n  function _setName(string memory newName) internal {\r\n    _name = newName;\r\n  }\r\n\r\n  /**\r\n   * @notice Update the symbol for the token\r\n   * @param newSymbol The new symbol for the token\r\n   */\r\n  function _setSymbol(string memory newSymbol) internal {\r\n    _symbol = newSymbol;\r\n  }\r\n\r\n  /**\r\n   * @notice Update the number of decimals for the token\r\n   * @param newDecimals The new number of decimals for the token\r\n   */\r\n  function _setDecimals(uint8 newDecimals) internal {\r\n    _decimals = newDecimals;\r\n  }\r\n}\r\n```\r\n\r\nIncentivizedERC20和标准ERC20很类似，不同之处在于两个，一个是balance使用UserState进行包装:\r\n```solidity\r\n/**\r\n   * @dev UserState - additionalData is a flexible field.\r\n   * ATokens and VariableDebtTokens use this field store the index of the\r\n   * user's last supply/withdrawal/borrow/repayment. StableDebtTokens use\r\n   * this field to store the user's stable rate.\r\n   */\r\n  struct UserState {\r\n    uint128 balance;\r\n    uint128 additionalData;\r\n  }\r\n  // Map of users address and their state data (userAddress => userStateData)\r\n  mapping(address => UserState) internal _userState;\r\n```\r\nUserState里保存的balance就是实际的balance，而additionalData其实是当前的利率的累加指数，如果是AToken，这个就是存款利率的累加指数，如果是DebtToken，就是贷款利率的累加指数。\r\n第二个不同是_transfer会调用IAaveIncentivesController相关方法:\r\n```solidity\r\nfunction _transfer(\r\n    address sender,\r\n    address recipient,\r\n    uint128 amount\r\n  ) internal virtual {\r\n    uint128 oldSenderBalance = _userState[sender].balance;\r\n    _userState[sender].balance = oldSenderBalance - amount;\r\n    uint128 oldRecipientBalance = _userState[recipient].balance;\r\n    _userState[recipient].balance = oldRecipientBalance + amount;\r\n\r\n    IAaveIncentivesController incentivesControllerLocal = _incentivesController;\r\n    if (address(incentivesControllerLocal) != address(0)) {\r\n      uint256 currentTotalSupply = _totalSupply;\r\n      incentivesControllerLocal.handleAction(sender, currentTotalSupply, oldSenderBalance);\r\n      if (sender != recipient) {\r\n        incentivesControllerLocal.handleAction(recipient, currentTotalSupply, oldRecipientBalance);\r\n      }\r\n    }\r\n  }\r\n```\r\n这里的IAaveIncentivesController其实是Aave里的RewardController（https://docs.aave.com/developers/periphery-contracts/rewardscontroller）\r\n这里回调了handleAction方法，实际是更新用户的持仓数量。\r\n\r\n## 2.2.2 MintableIncentivizedERC20\r\n```solidity\r\nabstract contract MintableIncentivizedERC20 is IncentivizedERC20 {\r\n  /**\r\n   * @dev Constructor.\r\n   * @param pool The reference to the main Pool contract\r\n   * @param name The name of the token\r\n   * @param symbol The symbol of the token\r\n   * @param decimals The number of decimals of the token\r\n   */\r\n  constructor(\r\n    IPool pool,\r\n    string memory name,\r\n    string memory symbol,\r\n    uint8 decimals\r\n  ) IncentivizedERC20(pool, name, symbol, decimals) {\r\n    // Intentionally left blank\r\n  }\r\n\r\n  /**\r\n   * @notice Mints tokens to an account and apply incentives if defined\r\n   * @param account The address receiving tokens\r\n   * @param amount The amount of tokens to mint\r\n   */\r\n  function _mint(address account, uint128 amount) internal virtual {\r\n    uint256 oldTotalSupply = _totalSupply;\r\n    _totalSupply = oldTotalSupply + amount;\r\n\r\n    uint128 oldAccountBalance = _userState[account].balance;\r\n    _userState[account].balance = oldAccountBalance + amount;\r\n\r\n    IAaveIncentivesController incentivesControllerLocal = _incentivesController;\r\n    if (address(incentivesControllerLocal) != address(0)) {\r\n      incentivesControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * @notice Burns tokens from an account and apply incentives if defined\r\n   * @param account The account whose tokens are burnt\r\n   * @param amount The amount of tokens to burn\r\n   */\r\n  function _burn(address account, uint128 amount) internal virtual {\r\n    uint256 oldTotalSupply = _totalSupply;\r\n    _totalSupply = oldTotalSupply - amount;\r\n\r\n    uint128 oldAccountBalance = _userState[account].balance;\r\n    _userState[account].balance = oldAccountBalance - amount;\r\n\r\n    IAaveIncentivesController incentivesControllerLocal = _incentivesController;\r\n\r\n    if (address(incentivesControllerLocal) != address(0)) {\r\n      incentivesControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);\r\n    }\r\n  }\r\n}\r\nFooter\r\n\r\n```\r\n我们可以看到MintableIncentivizedERC20继承了IncentivizedERC20，然后实现了mint和burn的功能，这两个方法会去更新_totalSupply和UserState里的balance，然后会调用IAaveIncentivesController去更新实际持仓量。\r\n\r\n## 2.2.3 ScaledBalanceTokenBase\r\n```solidity\r\n// SPDX-License-Identifier: BUSL-1.1\r\npragma solidity 0.8.10;\r\n\r\nimport {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';\r\nimport {Errors} from '../../libraries/helpers/Errors.sol';\r\nimport {WadRayMath} from '../../libraries/math/WadRayMath.sol';\r\nimport {IPool} from '../../../interfaces/IPool.sol';\r\nimport {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';\r\nimport {MintableIncentivizedERC20} from './MintableIncentivizedERC20.sol';\r\n\r\n/**\r\n * @title ScaledBalanceTokenBase\r\n * @author Aave\r\n * @notice Basic ERC20 implementation of scaled balance token\r\n */\r\nabstract contract ScaledBalanceTokenBase is MintableIncentivizedERC20, IScaledBalanceToken {\r\n  using WadRayMath for uint256;\r\n  using SafeCast for uint256;\r\n\r\n  /**\r\n   * @dev Constructor.\r\n   * @param pool The reference to the main Pool contract\r\n   * @param name The name of the token\r\n   * @param symbol The symbol of the token\r\n   * @param decimals The number of decimals of the token\r\n   */\r\n  constructor(\r\n    IPool pool,\r\n    string memory name,\r\n    string memory symbol,\r\n    uint8 decimals\r\n  ) MintableIncentivizedERC20(pool, name, symbol, decimals) {\r\n    // Intentionally left blank\r\n  }\r\n\r\n  /// @inheritdoc IScaledBalanceToken\r\n  function scaledBalanceOf(address user) external view override returns (uint256) {\r\n    return super.balanceOf(user);\r\n  }\r\n\r\n  /// @inheritdoc IScaledBalanceToken\r\n  function getScaledUserBalanceAndSupply(address user)\r\n    external\r\n    view\r\n    override\r\n    returns (uint256, uint256)\r\n  {\r\n    return (super.balanceOf(user), super.totalSupply());\r\n  }\r\n\r\n  /// @inheritdoc IScaledBalanceToken\r\n  function scaledTotalSupply() public view virtual override returns (uint256) {\r\n    return super.totalSupply();\r\n  }\r\n\r\n  /// @inheritdoc IScaledBalanceToken\r\n  function getPreviousIndex(address user) external view virtual override returns (uint256) {\r\n    return _userState[user].additionalData;\r\n  }\r\n\r\n  /**\r\n   * @notice Implements the basic logic to mint a scaled balance token.\r\n   * @param caller The address performing the mint\r\n   * @param onBehalfOf The address of the user that will receive the scaled tokens\r\n   * @param amount The amount of tokens getting minted\r\n   * @param index The next liquidity index of the reserve\r\n   * @return `true` if the the previous balance of the user was 0\r\n   */\r\n  function _mintScaled(\r\n    address caller,\r\n    address onBehalfOf,\r\n    uint256 amount,\r\n    uint256 index\r\n  ) internal returns (bool) {\r\n    uint256 amountScaled = amount.rayDiv(index);\r\n    require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);\r\n\r\n    uint256 scaledBalance = super.balanceOf(onBehalfOf);\r\n    uint256 balanceIncrease = scaledBalance.rayMul(index) -\r\n      scaledBalance.rayMul(_userState[onBehalfOf].additionalData);\r\n\r\n    _userState[onBehalfOf].additionalData = index.toUint128();\r\n\r\n    _mint(onBehalfOf, amountScaled.toUint128());\r\n\r\n    uint256 amountToMint = amount + balanceIncrease;\r\n    emit Transfer(address(0), onBehalfOf, amountToMint);\r\n    emit Mint(caller, onBehalfOf, amountToMint, balanceIncrease, index);\r\n\r\n    return (scaledBalance == 0);\r\n  }\r\n\r\n  /**\r\n   * @notice Implements the basic logic to burn a scaled balance token.\r\n   * @dev In some instances, a burn transaction will emit a mint event\r\n   * if the amount to burn is less than the interest that the user accrued\r\n   * @param user The user which debt is burnt\r\n   * @param target The address that will receive the underlying, if any\r\n   * @param amount The amount getting burned\r\n   * @param index The variable debt index of the reserve\r\n   */\r\n  function _burnScaled(\r\n    address user,\r\n    address target,\r\n    uint256 amount,\r\n    uint256 index\r\n  ) internal {\r\n    uint256 amountScaled = amount.rayDiv(index);\r\n    require(amountScaled != 0, Errors.INVALID_BURN_AMOUNT);\r\n\r\n    uint256 scaledBalance = super.balanceOf(user);\r\n    uint256 balanceIncrease = scaledBalance.rayMul(index) -\r\n      scaledBalance.rayMul(_userState[user].additionalData);\r\n\r\n    _userState[user].additionalData = index.toUint128();\r\n\r\n    _burn(user, amountScaled.toUint128());\r\n\r\n    if (balanceIncrease > amount) {\r\n      uint256 amountToMint = balanceIncrease - amount;\r\n      emit Transfer(address(0), user, amountToMint);\r\n      emit Mint(user, user, amountToMint, balanceIncrease, index);\r\n    } else {\r\n      uint256 amountToBurn = amount - balanceIncrease;\r\n      emit Transfer(user, address(0), amountToBurn);\r\n      emit Burn(user, target, amountToBurn, balanceIncrease, index);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * @notice Implements the basic logic to transfer scaled balance tokens between two users\r\n   * @dev It emits a mint event with the interest accrued per user\r\n   * @param sender The source address\r\n   * @param recipient The destination address\r\n   * @param amount The amount getting transferred\r\n   * @param index The next liquidity index of the reserve\r\n   */\r\n  function _transfer(\r\n    address sender,\r\n    address recipient,\r\n    uint256 amount,\r\n    uint256 index\r\n  ) internal {\r\n    uint256 senderScaledBalance = super.balanceOf(sender);\r\n    uint256 senderBalanceIncrease = senderScaledBalance.rayMul(index) -\r\n      senderScaledBalance.rayMul(_userState[sender].additionalData);\r\n\r\n    uint256 recipientScaledBalance = super.balanceOf(recipient);\r\n    uint256 recipientBalanceIncrease = recipientScaledBalance.rayMul(index) -\r\n      recipientScaledBalance.rayMul(_userState[recipient].additionalData);\r\n\r\n    _userState[sender].additionalData = index.toUint128();\r\n    _userState[recipient].additionalData = index.toUint128();\r\n\r\n    super._transfer(sender, recipient, amount.rayDiv(index).toUint128());\r\n\r\n    if (senderBalanceIncrease > 0) {\r\n      emit Transfer(address(0), sender, senderBalanceIncrease);\r\n      emit Mint(_msgSender(), sender, senderBalanceIncrease, senderBalanceIncrease, index);\r\n    }\r\n\r\n    if (sender != recipient && recipientBalanceIncrease > 0) {\r\n      emit Transfer(address(0), recipient, recipientBalanceIncrease);\r\n      emit Mint(_msgSender(), recipient, recipientBalanceIncrease, recipientBalanceIncrease, index);\r\n    }\r\n\r\n    emit Transfer(sender, recipient, amount);\r\n  }\r\n}\r\n```\r\nScaledBalanceTokenBase继承了MintableIncentivizedERC20，并且实现了三个新方法：\\_mintScaled、\\_burnScaled和 \\_transfer\r\na)\\_mintScaled\r\n```solidity\r\nfunction _mintScaled(\r\n    address caller,\r\n    address onBehalfOf,\r\n    uint256 amount,\r\n    uint256 index\r\n  ) internal returns (bool)\r\n```\r\n第一个传参caller是调用者，第二个传参onBehalfOf是实际受影响的用户地址，第三个传参amount是存入的底层资产的数量，第四个传参是index，也就是当前的存款利率累加指数。\r\n接下来我们可以看到，实际需要被mint的数量`amountScaled`，其实是用amount去除以累加指数index：\r\n```solidity\r\nuint256 amountScaled = amount.rayDiv(index);\r\nrequire(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);\r\n```\r\n如果有点金融知识的就能理解，这其实是将该资产按照index进行贴现处理。举个例子，当第一个用户存款100，这时候index是1，此时amountScaled = 100 / 1 = 100。过一段时间后，index变成了1.03，如果用户又存入了100，此时amountScaled = 100/1.03 = 97，此时用户总的balance = 100 + 97 = 197。之所以这么处理，是因为用户实际的本息和是用balance去乘以当前的index。当第二次存款的时候，用户正确的本息和应该是 100 * 1.03 + 100 = 203，如果不做贴现处理，第二次用户存入100后的本息和就变成了(100+100) * 1.03 = 206，是错误的，如果进行了贴现，本息和就变成了（100 + 100 / 1.03) * 1.03 = 103 + 100 = 203，这是正确的。\r\n算出`amountScaled`后直接调用_mint:\r\n```solidity\r\n_mint(onBehalfOf, amountScaled.toUint128());\r\n```\r\n其他代码都是为了处理事件，可以不看。\r\n\r\nb)\\_burnScaled\r\n\\_burnScaled和\\_mintScaled逻辑差不多，也是先进行贴现处理，然后burn掉。\r\n\r\nc) \\_transfer\r\n\\_transfer的核心代码就一行：\r\n```solidity\r\nsuper._transfer(sender, recipient, amount.rayDiv(index).toUint128());\r\n```\r\n其他都是为了处理事件而进行的计算\r\n\r\n## 2.3 EIP712Base\r\n```solidity\r\n// SPDX-License-Identifier: BUSL-1.1\r\npragma solidity 0.8.10;\r\n\r\n/**\r\n * @title EIP712Base\r\n * @author Aave\r\n * @notice Base contract implementation of EIP712.\r\n */\r\nabstract contract EIP712Base {\r\n  bytes public constant EIP712_REVISION = bytes('1');\r\n  bytes32 internal constant EIP712_DOMAIN =\r\n    keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');\r\n\r\n  // Map of address nonces (address => nonce)\r\n  mapping(address => uint256) internal _nonces;\r\n\r\n  bytes32 internal _domainSeparator;\r\n  uint256 internal immutable _chainId;\r\n\r\n  /**\r\n   * @dev Constructor.\r\n   */\r\n  constructor() {\r\n    _chainId = block.chainid;\r\n  }\r\n\r\n  /**\r\n   * @notice Get the domain separator for the token\r\n   * @dev Return cached value if chainId matches cache, otherwise recomputes separator\r\n   * @return The domain separator of the token at current chain\r\n   */\r\n  function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\r\n    if (block.chainid == _chainId) {\r\n      return _domainSeparator;\r\n    }\r\n    return _calculateDomainSeparator();\r\n  }\r\n\r\n  /**\r\n   * @notice Returns the nonce value for address specified as parameter\r\n   * @param owner The address for which the nonce is being returned\r\n   * @return The nonce value for the input address`\r\n   */\r\n  function nonces(address owner) public view virtual returns (uint256) {\r\n    return _nonces[owner];\r\n  }\r\n\r\n  /**\r\n   * @notice Compute the current domain separator\r\n   * @return The domain separator for the token\r\n   */\r\n  function _calculateDomainSeparator() internal view returns (bytes32) {\r\n    return\r\n      keccak256(\r\n        abi.encode(\r\n          EIP712_DOMAIN,\r\n          keccak256(bytes(_EIP712BaseId())),\r\n          keccak256(EIP712_REVISION),\r\n          block.chainid,\r\n          address(this)\r\n        )\r\n      );\r\n  }\r\n\r\n  /**\r\n   * @notice Returns the user readable name of signing domain (e.g. token name)\r\n   * @return The name of the signing domain\r\n   */\r\n  function _EIP712BaseId() internal view virtual returns (string memory);\r\n}\r\n```\r\n其实就是实现了EIP712，如果不理解EIP712，可以看这篇文章：https://learnblockchain.cn/2019/04/24/token-EIP712\r\n\r\n## 2.4 AToken\r\n```solidity\r\n// SPDX-License-Identifier: BUSL-1.1\r\npragma solidity 0.8.10;\r\n\r\nimport {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';\r\nimport {GPv2SafeERC20} from '../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';\r\nimport {SafeCast} from '../../dependencies/openzeppelin/contracts/SafeCast.sol';\r\nimport {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';\r\nimport {Errors} from '../libraries/helpers/Errors.sol';\r\nimport {WadRayMath} from '../libraries/math/WadRayMath.sol';\r\nimport {IPool} from '../../interfaces/IPool.sol';\r\nimport {IAToken} from '../../interfaces/IAToken.sol';\r\nimport {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';\r\nimport {IInitializableAToken} from '../../interfaces/IInitializableAToken.sol';\r\nimport {ScaledBalanceTokenBase} from './base/ScaledBalanceTokenBase.sol';\r\nimport {IncentivizedERC20} from './base/IncentivizedERC20.sol';\r\nimport {EIP712Base} from './base/EIP712Base.sol';\r\n\r\n/**\r\n * @title Aave ERC20 AToken\r\n * @author Aave\r\n * @notice Implementation of the interest bearing token for the Aave protocol\r\n */\r\ncontract AToken is VersionedInitializable, ScaledBalanceTokenBase, EIP712Base, IAToken {\r\n  using WadRayMath for uint256;\r\n  using SafeCast for uint256;\r\n  using GPv2SafeERC20 for IERC20;\r\n\r\n  bytes32 public constant PERMIT_TYPEHASH =\r\n    keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');\r\n\r\n  uint256 public constant ATOKEN_REVISION = 0x1;\r\n\r\n  address internal _treasury;\r\n  address internal _underlyingAsset;\r\n\r\n  /// @inheritdoc VersionedInitializable\r\n  function getRevision() internal pure virtual override returns (uint256) {\r\n    return ATOKEN_REVISION;\r\n  }\r\n\r\n  /**\r\n   * @dev Constructor.\r\n   * @param pool The address of the Pool contract\r\n   */\r\n  constructor(IPool pool)\r\n    ScaledBalanceTokenBase(pool, 'ATOKEN_IMPL', 'ATOKEN_IMPL', 0)\r\n    EIP712Base()\r\n  {\r\n    // Intentionally left blank\r\n  }\r\n\r\n  /// @inheritdoc IInitializableAToken\r\n  function initialize(\r\n    IPool initializingPool,\r\n    address treasury,\r\n    address underlyingAsset,\r\n    IAaveIncentivesController incentivesController,\r\n    uint8 aTokenDecimals,\r\n    string calldata aTokenName,\r\n    string calldata aTokenSymbol,\r\n    bytes calldata params\r\n  ) public virtual override initializer {\r\n    require(initializingPool == POOL, Errors.POOL_ADDRESSES_DO_NOT_MATCH);\r\n    _setName(aTokenName);\r\n    _setSymbol(aTokenSymbol);\r\n    _setDecimals(aTokenDecimals);\r\n\r\n    _treasury = treasury;\r\n    _underlyingAsset = underlyingAsset;\r\n    _incentivesController = incentivesController;\r\n\r\n    _domainSeparator = _calculateDomainSeparator();\r\n\r\n    emit Initialized(\r\n      underlyingAsset,\r\n      address(POOL),\r\n      treasury,\r\n      address(incentivesController),\r\n      aTokenDecimals,\r\n      aTokenName,\r\n      aTokenSymbol,\r\n      params\r\n    );\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function mint(\r\n    address caller,\r\n    address onBehalfOf,\r\n    uint256 amount,\r\n    uint256 index\r\n  ) external virtual override onlyPool returns (bool) {\r\n    return _mintScaled(caller, onBehalfOf, amount, index);\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function burn(\r\n    address from,\r\n    address receiverOfUnderlying,\r\n    uint256 amount,\r\n    uint256 index\r\n  ) external virtual override onlyPool {\r\n    _burnScaled(from, receiverOfUnderlying, amount, index);\r\n    if (receiverOfUnderlying != address(this)) {\r\n      IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount);\r\n    }\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function mintToTreasury(uint256 amount, uint256 index) external virtual override onlyPool {\r\n    if (amount == 0) {\r\n      return;\r\n    }\r\n    _mintScaled(address(POOL), _treasury, amount, index);\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function transferOnLiquidation(\r\n    address from,\r\n    address to,\r\n    uint256 value\r\n  ) external virtual override onlyPool {\r\n    // Being a normal transfer, the Transfer() and BalanceTransfer() are emitted\r\n    // so no need to emit a specific event here\r\n    _transfer(from, to, value, false);\r\n  }\r\n\r\n  /// @inheritdoc IERC20\r\n  function balanceOf(address user)\r\n    public\r\n    view\r\n    virtual\r\n    override(IncentivizedERC20, IERC20)\r\n    returns (uint256)\r\n  {\r\n    return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));\r\n  }\r\n\r\n  /// @inheritdoc IERC20\r\n  function totalSupply() public view virtual override(IncentivizedERC20, IERC20) returns (uint256) {\r\n    uint256 currentSupplyScaled = super.totalSupply();\r\n\r\n    if (currentSupplyScaled == 0) {\r\n      return 0;\r\n    }\r\n\r\n    return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function RESERVE_TREASURY_ADDRESS() external view override returns (address) {\r\n    return _treasury;\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function UNDERLYING_ASSET_ADDRESS() external view override returns (address) {\r\n    return _underlyingAsset;\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function transferUnderlyingTo(address target, uint256 amount) external virtual override onlyPool {\r\n    IERC20(_underlyingAsset).safeTransfer(target, amount);\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function handleRepayment(\r\n    address user,\r\n    address onBehalfOf,\r\n    uint256 amount\r\n  ) external virtual override onlyPool {\r\n    // Intentionally left blank\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function permit(\r\n    address owner,\r\n    address spender,\r\n    uint256 value,\r\n    uint256 deadline,\r\n    uint8 v,\r\n    bytes32 r,\r\n    bytes32 s\r\n  ) external override {\r\n    require(owner != address(0), Errors.ZERO_ADDRESS_NOT_VALID);\r\n    //solium-disable-next-line\r\n    require(block.timestamp <= deadline, Errors.INVALID_EXPIRATION);\r\n    uint256 currentValidNonce = _nonces[owner];\r\n    bytes32 digest = keccak256(\r\n      abi.encodePacked(\r\n        '\\x19\\x01',\r\n        DOMAIN_SEPARATOR(),\r\n        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))\r\n      )\r\n    );\r\n    require(owner == ecrecover(digest, v, r, s), Errors.INVALID_SIGNATURE);\r\n    _nonces[owner] = currentValidNonce + 1;\r\n    _approve(owner, spender, value);\r\n  }\r\n\r\n  /**\r\n   * @notice Transfers the aTokens between two users. Validates the transfer\r\n   * (ie checks for valid HF after the transfer) if required\r\n   * @param from The source address\r\n   * @param to The destination address\r\n   * @param amount The amount getting transferred\r\n   * @param validate True if the transfer needs to be validated, false otherwise\r\n   */\r\n  function _transfer(\r\n    address from,\r\n    address to,\r\n    uint256 amount,\r\n    bool validate\r\n  ) internal virtual {\r\n    address underlyingAsset = _underlyingAsset;\r\n\r\n    uint256 index = POOL.getReserveNormalizedIncome(underlyingAsset);\r\n\r\n    uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index);\r\n    uint256 toBalanceBefore = super.balanceOf(to).rayMul(index);\r\n\r\n    super._transfer(from, to, amount, index);\r\n\r\n    if (validate) {\r\n      POOL.finalizeTransfer(underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore);\r\n    }\r\n\r\n    emit BalanceTransfer(from, to, amount.rayDiv(index), index);\r\n  }\r\n\r\n  /**\r\n   * @notice Overrides the parent _transfer to force validated transfer() and transferFrom()\r\n   * @param from The source address\r\n   * @param to The destination address\r\n   * @param amount The amount getting transferred\r\n   */\r\n  function _transfer(\r\n    address from,\r\n    address to,\r\n    uint128 amount\r\n  ) internal virtual override {\r\n    _transfer(from, to, amount, true);\r\n  }\r\n\r\n  /**\r\n   * @dev Overrides the base function to fully implement IAToken\r\n   * @dev see `EIP712Base.DOMAIN_SEPARATOR()` for more detailed documentation\r\n   */\r\n  function DOMAIN_SEPARATOR() public view override(IAToken, EIP712Base) returns (bytes32) {\r\n    return super.DOMAIN_SEPARATOR();\r\n  }\r\n\r\n  /**\r\n   * @dev Overrides the base function to fully implement IAToken\r\n   * @dev see `EIP712Base.nonces()` for more detailed documentation\r\n   */\r\n  function nonces(address owner) public view override(IAToken, EIP712Base) returns (uint256) {\r\n    return super.nonces(owner);\r\n  }\r\n\r\n  /// @inheritdoc EIP712Base\r\n  function _EIP712BaseId() internal view override returns (string memory) {\r\n    return name();\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function rescueTokens(\r\n    address token,\r\n    address to,\r\n    uint256 amount\r\n  ) external override onlyPoolAdmin {\r\n    require(token != _underlyingAsset, Errors.UNDERLYING_CANNOT_BE_RESCUED);\r\n    IERC20(token).safeTransfer(to, amount);\r\n  }\r\n}\r\n```\r\nAToken继承了ScaledBalanceTokenBase并且重写了balanceOf方法：\r\n```solidity\r\nfunction balanceOf(address user)\r\n    public\r\n    view\r\n    virtual\r\n    override(IncentivizedERC20, IERC20)\r\n    returns (uint256)\r\n  {\r\n    return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));\r\n  }\r\n```\r\nsuper.balanceOf(user)返回的是实际的balance，这些balance都是进行贴现处理过的，POOL.getReserveNormalizedIncome(\\_underlyingAsset)返回的是当前的存款利率累加指数。两个相乘就是用户的本息和。随着时间的推移，由于贷款利息在不断增加，因此存款利率累加指数也会慢慢变大，因此用户AToken的balanceOf返回的值也会慢慢变大。这其实是违背了ERC20的标准的，因此很多其他协议在使用AToken的时候，又会进行一层封装，比如Balancer在使用AToken的时候，会将它转成一个static token，具体可以看他们的文档。\r\n\r\n重写了totalSupply方法：\r\n```solidity\r\nfunction totalSupply() public view virtual override(IncentivizedERC20, IERC20) returns (uint256) {\r\n    uint256 currentSupplyScaled = super.totalSupply();\r\n\r\n    if (currentSupplyScaled == 0) {\r\n      return 0;\r\n    }\r\n\r\n    return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));\r\n  }\r\n```\r\n原理和balanceOf一样。\r\n\r\n重写了mint和burn：\r\n```solidity\r\nfunction mint(\r\n    address caller,\r\n    address onBehalfOf,\r\n    uint256 amount,\r\n    uint256 index\r\n  ) external virtual override onlyPool returns (bool) {\r\n    return _mintScaled(caller, onBehalfOf, amount, index);\r\n  }\r\n\r\n  /// @inheritdoc IAToken\r\n  function burn(\r\n    address from,\r\n    address receiverOfUnderlying,\r\n    uint256 amount,\r\n    uint256 index\r\n  ) external virtual override onlyPool {\r\n    _burnScaled(from, receiverOfUnderlying, amount, index);\r\n    if (receiverOfUnderlying != address(this)) {\r\n      IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount);\r\n    }\r\n  }\r\n```\r\n底层调用\\_mintScaled和\\_burnScaled。\r\n\r\n重写了\\_transfer方法：\r\n```solidity\r\nfunction _transfer(\r\n    address from,\r\n    address to,\r\n    uint256 amount,\r\n    bool validate\r\n  ) internal virtual {\r\n    address underlyingAsset = _underlyingAsset;\r\n\r\n    uint256 index = POOL.getReserveNormalizedIncome(underlyingAsset);\r\n\r\n    uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index);\r\n    uint256 toBalanceBefore = super.balanceOf(to).rayMul(index);\r\n\r\n    super._transfer(from, to, amount, index);\r\n\r\n    if (validate) {\r\n      POOL.finalizeTransfer(underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore);\r\n    }\r\n\r\n    emit BalanceTransfer(from, to, amount.rayDiv(index), index);\r\n  }\r\n```\r\n使用的是ScaledBalanceTokenBase的\\_transfer方法，如果validate是true，还需要调用POOL.finalizeTransfer校验下用户是否可以转移，如果用户本身存在债务，就可能无法转移，否则会被清算。\r\n\r\n总的来说，Aave通过贴现的方式，将不同时间点的存款数量转化成初始时间点的存款数量，因此每份数量的底层资产对应的本息和，就可以直接用amount * index算出来，大大方便了计算和理解。"},"author":{"user":"https://learnblockchain.cn/people/2894","address":"0xBDF9255fD9A205a5Cd64DDa55E5Bc609975A6968"},"history":null,"timestamp":1678258228,"version":1}