diff --git a/contracts/interfaces/IERC20.sol b/contracts/interfaces/IERC20.sol new file mode 100644 index 00000000..af94806b --- /dev/null +++ b/contracts/interfaces/IERC20.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.8; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/contracts/interfaces/IERC20Detailed.sol b/contracts/interfaces/IERC20Detailed.sol index 89ac6f52..db0fbb98 100644 --- a/contracts/interfaces/IERC20Detailed.sol +++ b/contracts/interfaces/IERC20Detailed.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.6.8; -import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {IERC20} from './IERC20.sol'; interface IERC20Detailed is IERC20 { function name() external view returns (string memory); diff --git a/contracts/libraries/math/SafeMath.sol b/contracts/libraries/math/SafeMath.sol new file mode 100644 index 00000000..8ce54b91 --- /dev/null +++ b/contracts/libraries/math/SafeMath.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.8; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, 'SafeMath: addition overflow'); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return sub(a, b, 'SafeMath: subtraction overflow'); + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + require(b <= a, errorMessage); + uint256 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, 'SafeMath: multiplication overflow'); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return div(a, b, 'SafeMath: division by zero'); + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, errorMessage); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return mod(a, b, 'SafeMath: modulo by zero'); + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts with custom message when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + require(b != 0, errorMessage); + return a % b; + } +} diff --git a/contracts/misc/Address.sol b/contracts/misc/Address.sol new file mode 100644 index 00000000..2233df49 --- /dev/null +++ b/contracts/misc/Address.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.8; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // According to EIP-1052, 0x0 is the value returned for not-yet created accounts + // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned + // for accounts without code, i.e. `keccak256('')` + bytes32 codehash; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + // solhint-disable-next-line no-inline-assembly + assembly { + codehash := extcodehash(account) + } + return (codehash != accountHash && codehash != 0x0); + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, 'Address: insufficient balance'); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{value: amount}(''); + require(success, 'Address: unable to send value, recipient may have reverted'); + } +} diff --git a/contracts/misc/Context.sol b/contracts/misc/Context.sol new file mode 100644 index 00000000..7208e0c4 --- /dev/null +++ b/contracts/misc/Context.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.8; + +/* + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with GSN meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal virtual view returns (address payable) { + return msg.sender; + } + + function _msgData() internal virtual view returns (bytes memory) { + this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 + return msg.data; + } +} diff --git a/contracts/misc/SafeERC20.sol b/contracts/misc/SafeERC20.sol new file mode 100644 index 00000000..d1fa0c94 --- /dev/null +++ b/contracts/misc/SafeERC20.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.6.8; + +import {IERC20} from "../interfaces/IERC20.sol"; +import {SafeMath} from "../libraries/math/SafeMath.sol"; +import {Address} from "./Address.sol"; + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using SafeMath for uint256; + using Address for address; + + function safeTransfer(IERC20 token, address to, uint256 value) internal { + callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { + callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + function safeApprove(IERC20 token, address spender, uint256 value) internal { + require((value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function callOptionalReturn(IERC20 token, bytes memory data) private { + require(address(token).isContract(), "SafeERC20: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = address(token).call(data); + require(success, "SafeERC20: low-level call failed"); + + if (returndata.length > 0) { // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} diff --git a/contracts/mocks/upgradeability/MockAToken.sol b/contracts/mocks/upgradeability/MockAToken.sol index e9fd3f51..05f0db4e 100644 --- a/contracts/mocks/upgradeability/MockAToken.sol +++ b/contracts/mocks/upgradeability/MockAToken.sol @@ -3,7 +3,6 @@ pragma solidity ^0.6.8; import {AToken} from '../../tokenization/AToken.sol'; import {LendingPool} from '../../lendingpool/LendingPool.sol'; -import '@nomiclabs/buidler/console.sol'; contract MockAToken is AToken { constructor( @@ -22,8 +21,8 @@ contract MockAToken is AToken { string calldata _tokenName, string calldata _tokenSymbol ) external virtual override initializer { - _name = _tokenName; - _symbol = _tokenSymbol; - _setupDecimals(_underlyingAssetDecimals); + _setName(_tokenName); + _setSymbol(_tokenSymbol); + _setDecimals(_underlyingAssetDecimals); } } diff --git a/contracts/mocks/upgradeability/MockStableDebtToken.sol b/contracts/mocks/upgradeability/MockStableDebtToken.sol index 37e3e277..52e7a99f 100644 --- a/contracts/mocks/upgradeability/MockStableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockStableDebtToken.sol @@ -3,7 +3,6 @@ pragma solidity ^0.6.8; import {StableDebtToken} from '../../tokenization/StableDebtToken.sol'; import {LendingPool} from '../../lendingpool/LendingPool.sol'; -import '@nomiclabs/buidler/console.sol'; contract MockStableDebtToken is StableDebtToken { constructor( diff --git a/contracts/mocks/upgradeability/MockVariableDebtToken.sol b/contracts/mocks/upgradeability/MockVariableDebtToken.sol index be447dea..cb46d4d8 100644 --- a/contracts/mocks/upgradeability/MockVariableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockVariableDebtToken.sol @@ -3,7 +3,6 @@ pragma solidity ^0.6.8; import {VariableDebtToken} from '../../tokenization/VariableDebtToken.sol'; import {LendingPool} from '../../lendingpool/LendingPool.sol'; -import '@nomiclabs/buidler/console.sol'; contract MockVariableDebtToken is VariableDebtToken { constructor( diff --git a/contracts/tokenization/AToken.sol b/contracts/tokenization/AToken.sol index 3ec3eac5..de178819 100644 --- a/contracts/tokenization/AToken.sol +++ b/contracts/tokenization/AToken.sol @@ -5,11 +5,12 @@ import {ERC20} from './ERC20.sol'; import {LendingPool} from '../lendingpool/LendingPool.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; -import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import { VersionedInitializable } from '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol'; -import {IAToken, IERC20} from './interfaces/IAToken.sol'; +import {IAToken} from './interfaces/IAToken.sol'; +import {IERC20} from '../interfaces/IERC20.sol'; +import {SafeERC20} from "../misc/SafeERC20.sol"; /** * @title Aave ERC20 AToken @@ -49,7 +50,7 @@ contract AToken is VersionedInitializable, ERC20, IAToken { address underlyingAssetAddress, string memory tokenName, string memory tokenSymbol - ) public ERC20(tokenName, tokenSymbol) { + ) public ERC20(tokenName, tokenSymbol, 18) { _pool = pool; UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; } @@ -63,9 +64,9 @@ contract AToken is VersionedInitializable, ERC20, IAToken { string calldata tokenName, string calldata tokenSymbol ) external virtual initializer { - _name = tokenName; - _symbol = tokenSymbol; - _setupDecimals(underlyingAssetDecimals); + _setName(tokenName); + _setSymbol(tokenSymbol); + _setDecimals(underlyingAssetDecimals); } /** diff --git a/contracts/tokenization/ERC20.sol b/contracts/tokenization/ERC20.sol index 59caf6e8..f2902ce4 100644 --- a/contracts/tokenization/ERC20.sol +++ b/contracts/tokenization/ERC20.sol @@ -1,124 +1,88 @@ // SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.8; +pragma solidity 0.6.8; -import {Context} from '@openzeppelin/contracts/GSN/Context.sol'; -import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; -import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol'; +import {Context} from '../misc/Context.sol'; +import {IERC20} from '../interfaces/IERC20.sol'; +import {IERC20Detailed} from '../interfaces/IERC20Detailed.sol'; +import {SafeMath} from '../libraries/math/SafeMath.sol'; /** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * For a generic mechanism see {ERC20MinterPauser}. - * - * TIP: For a detailed writeup see our guide - * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * We have followed general OpenZeppelin guidelines: functions revert instead - * of returning `false` on failure. This behavior is nonetheless conventional - * and does not conflict with the expectations of ERC20 applications. - * - * Additionally, an {Approval} event is emitted on calls to {transferFrom}. - * This allows applications to reconstruct the allowance for all accounts just - * by listening to said events. Other implementations of the EIP may not emit - * these events, as it isn't required by the specification. - * - * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} - * functions have been added to mitigate the well-known issues around setting - * allowances. See {IERC20-approve}. - */ -contract ERC20 is Context, IERC20 { + * @title ERC20 + * @notice Basic ERC20 implementation + * @author Aave + **/ +contract ERC20 is Context, IERC20, IERC20Detailed { using SafeMath for uint256; mapping(address => uint256) private _balances; - mapping(address => mapping(address => uint256)) private _allowances; - uint256 private _totalSupply; - - string internal _name; - string internal _symbol; + string private _name; + string private _symbol; uint8 private _decimals; - /** - * @dev Sets the values for {name} and {symbol}, initializes {decimals} with - * a default value of 18. - * - * To select a different value for {decimals}, use {_setupDecimals}. - * - * All three of these values are immutable: they can only be set once during - * construction. - */ - constructor(string memory name, string memory symbol) public { + constructor( + string memory name, + string memory symbol, + uint8 decimals + ) public { _name = name; _symbol = symbol; - _decimals = 18; + _decimals = decimals; } /** - * @dev Returns the name of the token. - */ - function name() public view returns (string memory) { + * @return the name of the token + **/ + function name() public override view returns (string memory) { return _name; } /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view returns (string memory) { + * @return the symbol of the token + **/ + function symbol() public override view returns (string memory) { return _symbol; } /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5,05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is - * called. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view returns (uint8) { + * @return the decimals of the token + **/ + function decimals() public override view returns (uint8) { return _decimals; } /** - * @dev See {IERC20-totalSupply}. - */ + * @return the total supply of the token + **/ function totalSupply() public virtual override view returns (uint256) { return _totalSupply; } /** - * @dev See {IERC20-balanceOf}. - */ + * @return the balance of the token + **/ function balanceOf(address account) public virtual override view returns (uint256) { return _balances[account]; } /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - `recipient` cannot be the zero address. - * - the caller must have a balance of at least `amount`. - */ + * @dev executes a transfer of tokens from msg.sender to recipient + * @param recipient the recipient of the tokens + * @param amount the amount of tokens being transferred + * @return true if the transfer succeeds, false otherwise + **/ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** - * @dev See {IERC20-allowance}. - */ + * @dev returns the allowance of spender on the tokens owned by owner + * @param owner the owner of the tokens + * @param spender the user allowed to spend the owner's tokens + * @return the amount of owner's tokens spender is allowed to spend + **/ function allowance(address owner, address spender) public virtual @@ -130,29 +94,22 @@ contract ERC20 is Context, IERC20 { } /** - * @dev See {IERC20-approve}. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ + * @dev allows spender to spend the tokens owned by msg.sender + * @param spender the user allowed to spend msg.sender tokens + * @return true + **/ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** - * @dev See {IERC20-transferFrom}. - * - * Emits an {Approval} event indicating the updated allowance. This is not - * required by the EIP. See the note at the beginning of {ERC20}; - * - * Requirements: - * - `sender` and `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. - * - the caller must have allowance for ``sender``'s tokens of at least - * `amount`. - */ + * @dev executes a transfer of token from sender to recipient, if msg.sender is allowed to do so + * @param sender the owner of the tokens + * @param recipient the recipient of the tokens + * @param amount the amount of tokens being transferred + * @return true if the transfer succeeds, false otherwise + **/ function transferFrom( address sender, address recipient, @@ -168,36 +125,22 @@ contract ERC20 is Context, IERC20 { } /** - * @dev Atomically increases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ + * @dev increases the allowance of spender to spend msg.sender tokens + * @param spender the user allowed to spend on behalf of msg.sender + * @param addedValue the amount being added to the allowance + * @return true + **/ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `spender` must have allowance for the caller of at least - * `subtractedValue`. - */ + * @dev decreases the allowance of spender to spend msg.sender tokens + * @param spender the user allowed to spend on behalf of msg.sender + * @param subtractedValue the amount being subtracted to the allowance + * @return true + **/ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual @@ -214,20 +157,6 @@ contract ERC20 is Context, IERC20 { return true; } - /** - * @dev Moves tokens `amount` from `sender` to `recipient`. - * - * This is internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event. - * - * Requirements: - * - * - `sender` cannot be the zero address. - * - `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. - */ function _transfer( address sender, address recipient, @@ -243,15 +172,6 @@ contract ERC20 is Context, IERC20 { emit Transfer(sender, recipient, amount); } - /** @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * Emits a {Transfer} event with `from` set to the zero address. - * - * Requirements - * - * - `to` cannot be the zero address. - */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), 'ERC20: mint to the zero address'); @@ -262,17 +182,6 @@ contract ERC20 is Context, IERC20 { emit Transfer(address(0), account, amount); } - /** - * @dev Destroys `amount` tokens from `account`, reducing the - * total supply. - * - * Emits a {Transfer} event with `to` set to the zero address. - * - * Requirements - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), 'ERC20: burn from the zero address'); @@ -283,19 +192,6 @@ contract ERC20 is Context, IERC20 { emit Transfer(account, address(0), amount); } - /** - * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. - * - * This is internal function is equivalent to `approve`, and can be used to - * e.g. set automatic allowances for certain subsystems, etc. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `owner` cannot be the zero address. - * - `spender` cannot be the zero address. - */ function _approve( address owner, address spender, @@ -308,31 +204,18 @@ contract ERC20 is Context, IERC20 { emit Approval(owner, spender, amount); } - /** - * @dev Sets {decimals} to a value other than the default one of 18. - * - * WARNING: This function should only be called from the constructor. Most - * applications that interact with token contracts will not expect - * {decimals} to ever change, and may work incorrectly if it does. - */ - function _setupDecimals(uint8 decimals_) internal { - _decimals = decimals_; + function _setName(string memory newName) internal { + _name = newName; + } + + function _setSymbol(string memory newSymbol) internal { + _symbol = newSymbol; + } + + function _setDecimals(uint8 newDecimals) internal { + _decimals = newDecimals; } - /** - * @dev Hook that is called before any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be to transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ function _beforeTokenTransfer( address from, address to, diff --git a/contracts/tokenization/StableDebtToken.sol b/contracts/tokenization/StableDebtToken.sol index 318c7e04..447505cc 100644 --- a/contracts/tokenization/StableDebtToken.sol +++ b/contracts/tokenization/StableDebtToken.sol @@ -20,7 +20,6 @@ import {IStableDebtToken} from './interfaces/IStableDebtToken.sol'; * **/ contract StableDebtToken is IStableDebtToken, DebtTokenBase { - using SafeMath for uint256; using WadRayMath for uint256; uint256 public constant DEBT_TOKEN_REVISION = 0x1; @@ -78,7 +77,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { * @return the accumulated debt of the user **/ function balanceOf(address account) public virtual override view returns (uint256) { - uint256 accountBalance = _balances[account]; + uint256 accountBalance = principalBalanceOf(account); if (accountBalance == 0) { return 0; } @@ -120,7 +119,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { uint256 balanceIncrease ) = _calculateBalanceIncrease(user); - vars.supplyBeforeMint = _totalSupply.add(balanceIncrease); + vars.supplyBeforeMint = totalSupply().add(balanceIncrease); vars.supplyAfterMint = vars.supplyBeforeMint.add(amount); vars.amountInRay = amount.wadToRay(); @@ -167,7 +166,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { uint256 balanceIncrease ) = _calculateBalanceIncrease(user); - uint256 supplyBeforeBurn = _totalSupply.add(balanceIncrease); + uint256 supplyBeforeBurn = totalSupply().add(balanceIncrease); uint256 supplyAfterBurn = supplyBeforeBurn.sub(amount); if (supplyAfterBurn == 0) { diff --git a/contracts/tokenization/VariableDebtToken.sol b/contracts/tokenization/VariableDebtToken.sol index 9a4ffd2c..238da0b1 100644 --- a/contracts/tokenization/VariableDebtToken.sol +++ b/contracts/tokenization/VariableDebtToken.sol @@ -15,7 +15,6 @@ import {IVariableDebtToken} from './interfaces/IVariableDebtToken.sol'; * @dev does not inherit from IERC20 to save in contract size **/ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { - using SafeMath for uint256; using WadRayMath for uint256; uint256 public constant DEBT_TOKEN_REVISION = 0x1; @@ -42,7 +41,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { * @return the debt balance of the user **/ function balanceOf(address user) public virtual override view returns (uint256) { - uint256 userBalance = _balances[user]; + uint256 userBalance = principalBalanceOf(user); if (userBalance == 0) { return 0; } @@ -50,7 +49,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { return userBalance .wadToRay() - .rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress)) + .rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET)) .rayDiv(_userIndexes[user]) .rayToWad(); } @@ -78,7 +77,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { _mint(user, amount.add(balanceIncrease)); - uint256 newUserIndex = _pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress); + uint256 newUserIndex = POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET); _userIndexes[user] = newUserIndex; emit MintDebt(user, amount, previousBalance, currentBalance, balanceIncrease, newUserIndex); @@ -105,7 +104,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { uint256 newUserIndex = 0; //if user not repaid everything if (currentBalance != amount) { - newUserIndex = _pool.getReserveNormalizedVariableDebt(_underlyingAssetAddress); + newUserIndex = POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET); } _userIndexes[user] = newUserIndex; diff --git a/contracts/tokenization/base/DebtTokenBase.sol b/contracts/tokenization/base/DebtTokenBase.sol index 33d7222d..9aee76a5 100644 --- a/contracts/tokenization/base/DebtTokenBase.sol +++ b/contracts/tokenization/base/DebtTokenBase.sol @@ -5,185 +5,119 @@ import {Context} from '@openzeppelin/contracts/GSN/Context.sol'; import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol'; import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPool} from '../../interfaces/ILendingPool.sol'; -import { - VersionedInitializable -} from '../../libraries/openzeppelin-upgradeability/VersionedInitializable.sol'; -import {IERC20Detailed} from '../../interfaces/IERC20Detailed.sol'; +import {VersionedInitializable} from '../../libraries/openzeppelin-upgradeability/VersionedInitializable.sol'; +import {ERC20} from '../ERC20.sol'; import {Errors} from '../../libraries/helpers/Errors.sol'; /** - * @title contract DebtTokenBase + * @title DebtTokenBase + * @notice Base contract for different types of debt tokens, like StableDebtToken or VariableDebtToken * @author Aave - * @notice base contract for StableDebtToken and VariableDebtToken */ -abstract contract DebtTokenBase is IERC20Detailed, VersionedInitializable { - using SafeMath for uint256; +abstract contract DebtTokenBase is ERC20, VersionedInitializable { - uint256 internal _totalSupply; - - string internal _name; - string internal _symbol; - uint8 internal _decimals; - address internal immutable _underlyingAssetAddress; - - ILendingPool internal immutable _pool; - mapping(address => uint256) internal _balances; + address internal immutable UNDERLYING_ASSET; + ILendingPool internal immutable POOL; /** - * @dev only lending pool can call functions marked by this modifier + * @dev Only lending pool can call functions marked by this modifier **/ modifier onlyLendingPool { - require(msg.sender == address(_pool), Errors.CALLER_MUST_BE_LENDING_POOL); + require(msg.sender == address(POOL), Errors.CALLER_MUST_BE_LENDING_POOL); _; } + /** + * @dev The metadata of the token will be set on the proxy, that the reason of + * passing "NULL" and 0 as metadata + */ constructor( address pool, address underlyingAssetAddress, string memory name, - string memory symbol - ) public { - _pool = ILendingPool(pool); - _underlyingAssetAddress = underlyingAssetAddress; - _name = name; - _symbol = symbol; + string memory symbol + ) public ERC20(name, symbol, 18) { + POOL = ILendingPool(pool); + UNDERLYING_ASSET = underlyingAssetAddress; } /** - * @dev initializes the debt token. - * @param name the name of the token - * @param symbol the symbol of the token - * @param decimals the decimals of the token + * @dev Initializes the debt token. + * @param name The name of the token + * @param symbol The symbol of the token + * @param decimals The decimals of the token */ function initialize( uint8 decimals, string memory name, string memory symbol ) public initializer { - _name = name; - _symbol = symbol; - _decimals = decimals; - } - - function name() public override view returns (string memory) { - return _name; - } - - function symbol() public override view returns (string memory) { - return _symbol; - } - - function decimals() public override view returns (uint8) { - return _decimals; - } - - function totalSupply() public override view returns (uint256) { - return _totalSupply; + _setName(name); + _setSymbol(symbol); + _setDecimals(decimals); } function underlyingAssetAddress() public view returns (address) { - return _underlyingAssetAddress; + return UNDERLYING_ASSET; } /** - * @dev calculates the accumulated debt balance of the user - * @return the debt balance of the user - **/ - function balanceOf(address user) public virtual override view returns (uint256); - - /** - * @dev returns the principal debt balance of the user from - * @return the debt balance of the user since the last burn/mint action + * @dev Returns the principal debt balance of the user from + * @return The debt balance of the user since the last burn/mint action **/ function principalBalanceOf(address user) public view returns (uint256) { - return _balances[user]; + return super.balanceOf(user); } /** - * @dev basic accounting for the mint action - * @dev _user the target user of the minting action - * @dev _amount the amount to mint - **/ - function _mint(address user, uint256 amount) internal { - _totalSupply = _totalSupply.add(amount); - _balances[user] = _balances[user].add(amount); - } - - /** - * @dev basic accounting for the burn action - * @dev _user the target user of the burning action - * @dev _amount the amount to burn - **/ - function _burn(address user, uint256 amount) internal { - _totalSupply = _totalSupply.sub(amount); - _balances[user] = _balances[user].sub(amount); - } - - /** - * @dev being non transferrable, the debt token does not implement any of the + * @dev Being non transferrable, the debt token does not implement any of the * standard ERC20 functions for transfer and allowance. **/ - function transfer(address recipient, uint256 amount) external virtual override returns (bool) { + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + recipient; amount; revert('TRANSFER_NOT_SUPPORTED'); } - function allowance(address owner, address spender) - external - virtual - override - view - returns (uint256) - { + function allowance(address owner, address spender) public virtual override view returns (uint256) { + owner; spender; revert('ALLOWANCE_NOT_SUPPORTED'); } - function approve(address spender, uint256 amount) external virtual override returns (bool) { + function approve(address spender, uint256 amount) public virtual override returns (bool) { + spender; amount; revert('APPROVAL_NOT_SUPPORTED'); } - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external virtual override returns (bool) { + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + sender; recipient; amount; revert('TRANSFER_NOT_SUPPORTED'); } - function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool) { + function increaseAllowance(address spender, uint256 addedValue) public virtual override returns (bool) { + spender; addedValue; revert('ALLOWANCE_NOT_SUPPORTED'); } - function decreaseAllowance(address spender, uint256 subtractedValue) - external - virtual - returns (bool) - { + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual override returns (bool) { + spender; subtractedValue; revert('ALLOWANCE_NOT_SUPPORTED'); } /** - * @dev calculates the increase in balance since the last user interaction - * @param user the address of the user for which the interest is being accumulated - * @return the previous principal balance, the new principal balance, the balance increase + * @dev Calculates the increase in balance since the last user interaction + * @param user The address of the user for which the interest is being accumulated + * @return The previous principal balance, the new principal balance, the balance increase * and the new user index **/ - function _calculateBalanceIncrease(address user) - internal - view - returns ( - uint256, - uint256, - uint256 - ) - { - uint256 previousPrincipalBalance = _balances[user]; + function _calculateBalanceIncrease(address user) internal view returns (uint256, uint256, uint256) { + uint256 previousPrincipalBalance = principalBalanceOf(user); if (previousPrincipalBalance == 0) { return (0, 0, 0); } - //calculate the accrued interest since the last accumulation + // Calculation of the accrued interest since the last accumulation uint256 balanceIncrease = balanceOf(user).sub(previousPrincipalBalance); return ( diff --git a/contracts/tokenization/interfaces/IAToken.sol b/contracts/tokenization/interfaces/IAToken.sol index 868f81dc..65ad0cfa 100644 --- a/contracts/tokenization/interfaces/IAToken.sol +++ b/contracts/tokenization/interfaces/IAToken.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.6.8; -import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {IERC20} from '../../interfaces/IERC20.sol'; interface IAToken is IERC20 { /**