// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {DebtTokenBase} from './base/DebtTokenBase.sol'; import {ILendingPool} from '../../interfaces/ILendingPool.sol'; import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; /** * @title VariableDebtToken * @notice Implements a variable debt token to track the borrowing positions of users * at variable rate mode * @author Aave **/ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { using WadRayMath for uint256; bytes public constant EIP712_REVISION = bytes('1'); bytes32 internal constant EIP712_DOMAIN = keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'); bytes32 public constant PERMIT_DELEGATION_TYPEHASH = keccak256( 'PermitDelegation(address delegator,address delegatee,uint256 value,uint256 nonce,uint256 deadline)' ); uint256 public constant DEBT_TOKEN_REVISION = 0x2; ILendingPool internal _pool; address internal _underlyingAsset; IAaveIncentivesController internal _incentivesController; mapping(address => uint256) public _nonces; bytes32 public DOMAIN_SEPARATOR; /** * @dev Initializes the debt token. * @param pool The address of the lending pool where this aToken will be used * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) * @param incentivesController The smart contract managing potential incentives distribution * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's * @param debtTokenName The name of the token * @param debtTokenSymbol The symbol of the token */ function initialize( ILendingPool pool, address underlyingAsset, IAaveIncentivesController incentivesController, uint8 debtTokenDecimals, string memory debtTokenName, string memory debtTokenSymbol, bytes calldata params ) public override initializer { uint256 chainId; //solium-disable-next-line assembly { chainId := chainid() } _setName(debtTokenName); _setSymbol(debtTokenSymbol); _setDecimals(debtTokenDecimals); _pool = pool; _underlyingAsset = underlyingAsset; _incentivesController = incentivesController; DOMAIN_SEPARATOR = keccak256( abi.encode( EIP712_DOMAIN, keccak256(bytes(debtTokenName)), keccak256(EIP712_REVISION), chainId, address(this) ) ); emit Initialized( underlyingAsset, address(pool), address(incentivesController), debtTokenDecimals, debtTokenName, debtTokenSymbol, params ); } /** * @dev Gets the revision of the stable debt token implementation * @return The debt token implementation revision **/ function getRevision() internal pure virtual override returns (uint256) { return DEBT_TOKEN_REVISION; } /** * @dev Calculates the accumulated debt balance of the user * @return The debt balance of the user **/ function balanceOf(address user) public view virtual override returns (uint256) { uint256 scaledBalance = super.balanceOf(user); if (scaledBalance == 0) { return 0; } return scaledBalance.rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAsset)); } /** * @dev Mints debt token to the `onBehalfOf` address * - Only callable by the LendingPool * @param user The address receiving the borrowed underlying, being the delegatee in case * of credit delegate, or same as `onBehalfOf` otherwise * @param onBehalfOf The address receiving the debt tokens * @param amount The amount of debt being minted * @param index The variable debt index of the reserve * @return `true` if the the previous balance of the user is 0 **/ function mint( address user, address onBehalfOf, uint256 amount, uint256 index ) external override onlyLendingPool returns (bool) { if (user != onBehalfOf) { _decreaseBorrowAllowance(onBehalfOf, user, amount); } uint256 previousBalance = super.balanceOf(onBehalfOf); uint256 amountScaled = amount.rayDiv(index); require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT); _mint(onBehalfOf, amountScaled); emit Transfer(address(0), onBehalfOf, amount); emit Mint(user, onBehalfOf, amount, index); return previousBalance == 0; } /** * @dev Burns user variable debt * - Only callable by the LendingPool * @param user The user whose debt is getting burned * @param amount The amount getting burned * @param index The variable debt index of the reserve **/ function burn( address user, uint256 amount, uint256 index ) external override onlyLendingPool { uint256 amountScaled = amount.rayDiv(index); require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT); _burn(user, amountScaled); emit Transfer(user, address(0), amount); emit Burn(user, amount, index); } /** * @dev implements the credit delegation with ERC712 signature * @param delegator The delegator of the credit * @param delegatee The delegatee that can use the credit * @param value The amount to be delegated * @param deadline The deadline timestamp, type(uint256).max for max deadline * @param v Signature param * @param s Signature param * @param r Signature param */ function permitDelegation( address delegator, address delegatee, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override { require(delegator != address(0), 'INVALID_DELEGATOR'); //solium-disable-next-line require(block.timestamp <= deadline, 'INVALID_EXPIRATION'); uint256 currentValidNonce = _nonces[delegator]; bytes32 digest = keccak256( abi.encodePacked( '\x19\x01', DOMAIN_SEPARATOR, keccak256( abi.encode( PERMIT_DELEGATION_TYPEHASH, delegator, delegatee, value, currentValidNonce, deadline ) ) ) ); require(delegator == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE'); _nonces[delegator] = currentValidNonce.add(1); _approveDelegation(delegator, delegatee, value); } /** * @dev Returns the principal debt balance of the user from * @return The debt balance of the user since the last burn/mint action **/ function scaledBalanceOf(address user) public view virtual override returns (uint256) { return super.balanceOf(user); } /** * @dev Returns the total supply of the variable debt token. Represents the total debt accrued by the users * @return The total supply **/ function totalSupply() public view virtual override returns (uint256) { return super.totalSupply().rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAsset)); } /** * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) * @return the scaled total supply **/ function scaledTotalSupply() public view virtual override returns (uint256) { return super.totalSupply(); } /** * @dev Returns the principal balance of the user and principal total supply. * @param user The address of the user * @return The principal balance of the user * @return The principal total supply **/ function getScaledUserBalanceAndSupply(address user) external view override returns (uint256, uint256) { return (super.balanceOf(user), super.totalSupply()); } /** * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) **/ function UNDERLYING_ASSET_ADDRESS() public view returns (address) { return _underlyingAsset; } /** * @dev Returns the address of the incentives controller contract **/ function getIncentivesController() external view override returns (IAaveIncentivesController) { return _getIncentivesController(); } /** * @dev Returns the address of the lending pool where this aToken is used **/ function POOL() public view returns (ILendingPool) { return _pool; } function _getIncentivesController() internal view override returns (IAaveIncentivesController) { return _incentivesController; } function _getUnderlyingAssetAddress() internal view override returns (address) { return _underlyingAsset; } function _getLendingPool() internal view override returns (ILendingPool) { return _pool; } }