// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {IERC20Details} from '../../../dependencies/openzeppelin/contracts/IERC20Details.sol'; import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol'; import {ILendingPool} from '../../../interfaces/ILendingPool.sol'; import {IInitializablePoolToken} from '../interfaces/IInitializablePoolToken.sol'; import {IPoolToken} from '../../../interfaces/IPoolToken.sol'; import {PoolTokenConfig} from '../interfaces/PoolTokenConfig.sol'; import {IBalanceHook} from '../../../interfaces/IBalanceHook.sol'; import {Errors} from '../../libraries/helpers/Errors.sol'; import {AccessHelper} from '../../../access/AccessHelper.sol'; import {AccessFlags} from '../../../access/AccessFlags.sol'; abstract contract PoolTokenBase is IERC20, Context, IInitializablePoolToken, IPoolToken, IERC20Details { using SafeMath for uint256; string private _name; string private _symbol; uint8 private _decimals; mapping(address => uint256) internal _balances; uint256 internal _totalSupply; ILendingPool internal _pool; address internal _underlyingAsset; IBalanceHook private _incentivesController; constructor( string memory name, string memory symbol, uint8 decimals ) public { _name = name; _symbol = symbol; _decimals = decimals; } function _initializeERC20( string memory name, string memory symbol, uint8 decimals ) internal { _name = name; _symbol = symbol; _decimals = decimals; } function name() public view override returns (string memory) { return _name; } function symbol() public view override returns (string memory) { return _symbol; } function decimals() public view override returns (uint8) { return _decimals; } /** * @dev Only lending pool can call functions marked by this modifier **/ modifier onlyLendingPool { require(_msgSender() == address(_pool), Errors.CT_CALLER_MUST_BE_LENDING_POOL); _; } modifier onlyRewardAdmin { require( AccessHelper.hasAllOf( _pool.getAccessController(), _msgSender(), AccessFlags.REWARD_CONFIG_ADMIN ), Errors.CT_CALLER_MUST_BE_REWARD_ADMIN ); _; } function _initializePoolToken( PoolTokenConfig memory config, string memory debtTokenName, string memory debtTokenSymbol, uint8 debtTokenDecimals, bytes calldata params ) internal { _pool = config.pool; _underlyingAsset = config.underlyingAsset; emit Initialized( config.underlyingAsset, address(config.pool), address(config.treasury), debtTokenName, debtTokenSymbol, debtTokenDecimals, params ); } /** * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) **/ function UNDERLYING_ASSET_ADDRESS() public view override returns (address) { return _underlyingAsset; } /** * @dev Returns the address of the lending pool where this aToken is used **/ function POOL() public view override returns (ILendingPool) { return _pool; } function handleBalanceUpdate( address holder, uint256 oldBalance, uint256 newBalance, uint256 providerSupply ) internal virtual { IBalanceHook hook = _incentivesController; if (hook == IBalanceHook(0)) { return; } hook.handleBalanceUpdate(getIncentivesToken(), holder, oldBalance, newBalance, providerSupply); } function handleScaledBalanceUpdate( address holder, uint256 oldBalance, uint256 newBalance, uint256 providerSupply, uint256 scale ) internal { IBalanceHook hook = _incentivesController; if (hook == IBalanceHook(0)) { return; } hook.handleScaledBalanceUpdate( getIncentivesToken(), holder, oldBalance, newBalance, providerSupply, scale ); } function getIncentivesToken() internal view virtual returns (address) { return address(this); } function _setIncentivesController(address hook) internal virtual { _incentivesController = IBalanceHook(hook); } /** * @dev Updates the address of the incentives controller contract **/ function setIncentivesController(address hook) external override onlyRewardAdmin { _setIncentivesController(hook); } /** * @dev Returns the address of the incentives controller contract **/ function getIncentivesController() public view returns (IBalanceHook) { return _incentivesController; } function increaseAllowance(address, uint256) public virtual returns (bool); function decreaseAllowance(address, uint256) public virtual returns (bool); function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } function _mintBalance( address account, uint256 amount, uint256 scale ) internal { require(account != address(0), 'ERC20: mint to the zero address'); _beforeTokenTransfer(address(0), account, amount); uint256 total = _totalSupply; total = total.add(amount); _totalSupply = total; uint256 oldAccountBalance = _balances[account]; uint256 newAccountBalance = oldAccountBalance.add(amount); _balances[account] = newAccountBalance; handleScaledBalanceUpdate(account, oldAccountBalance, newAccountBalance, total, scale); } function _burnBalance( address account, uint256 amount, uint256 scale ) internal { require(account != address(0), 'ERC20: burn from the zero address'); _beforeTokenTransfer(account, address(0), amount); uint256 total = _totalSupply; total = total.sub(amount); _totalSupply = total; uint256 oldAccountBalance = _balances[account]; uint256 newAccountBalance = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'); _balances[account] = newAccountBalance; handleScaledBalanceUpdate(account, oldAccountBalance, newAccountBalance, total, scale); } function _transferBalance( address sender, address recipient, uint256 amount, uint256 scale ) internal { require(sender != address(0), 'ERC20: transfer from the zero address'); require(recipient != address(0), 'ERC20: transfer to the zero address'); _beforeTokenTransfer(sender, recipient, amount); uint256 oldSenderBalance = _balances[sender]; uint256 newSenderBalance = oldSenderBalance.sub(amount, 'ERC20: transfer amount exceeds balance'); _balances[sender] = newSenderBalance; uint256 oldRecipientBalance = _balances[recipient]; uint256 newRecipientBalance = oldRecipientBalance.add(amount); _balances[recipient] = newRecipientBalance; IBalanceHook hook = _incentivesController; if (address(hook) != address(0)) { address token = getIncentivesToken(); uint256 currentTotalSupply = _totalSupply; hook.handleScaledBalanceUpdate( token, sender, oldSenderBalance, newSenderBalance, currentTotalSupply, scale ); if (sender != recipient) { hook.handleScaledBalanceUpdate( token, recipient, oldRecipientBalance, newRecipientBalance, currentTotalSupply, scale ); } } } function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }