Transaction Hash:
Block:
12054386 at Mar-17-2021 05:56:13 AM +UTC
Transaction Fee:
0.0251667 ETH
$97.32
Gas Used:
186,420 Gas / 135 Gwei
Emitted Events:
148 |
VaultProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000798afe1a6131eba2c6c33e70842e3584fb05dcb0, 000000000000000000000000000000000000000000000000000000003c3b67df )
|
149 |
TetherToken.Transfer( from=[Sender] 0x798afe1a6131eba2c6c33e70842e3584fb05dcb0, to=[Receiver] VaultProxy, value=1010526273 )
|
150 |
VaultProxy.0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c( 0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c, 0x000000000000000000000000798afe1a6131eba2c6c33e70842e3584fb05dcb0, 000000000000000000000000000000000000000000000000000000003c3b6841 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x52bc44d5...b7d7bE3b5
Miner
| (Nanopool) | 3,635.339423571302786564 Eth | 3,635.364590271302786564 Eth | 0.0251667 | |
0x798AfE1a...4Fb05DCb0 |
0.206658955 Eth
Nonce: 26
|
0.181492255 Eth
Nonce: 27
| 0.0251667 | ||
0xdAC17F95...13D831ec7 | |||||
0xfd9EeC2B...7934B51A3 |
Execution Trace
VaultProxy.b6b55f25( )

Vault.deposit( amount=1010526273 )
-
IdleStrategyUSDTMainnet.STATICCALL( )
IdleStrategyUSDTMainnet.STATICCALL( )
IdleTokenHelper.getRedeemPrice( idleYieldToken=0xF34842d05A1c888Ca02769A633DF37177415C2f8 ) => ( redeemPrice=1040760 )
AdminUpgradeabilityProxy.0df94ef2( )
-
IdleTokenGovernance.userAvgPrices( 0x7c91e67910F8DBb947F95f41Ec76dAAc1C2Dc08e ) => ( 1040571 )
-
AdminUpgradeabilityProxy.STATICCALL( )
IdleTokenGovernance.DELEGATECALL( )
-
TetherToken.balanceOf( who=0xF34842d05A1c888Ca02769A633DF37177415C2f8 ) => ( 92412563378 )
-
CErc20Delegator.balanceOf( owner=0xF34842d05A1c888Ca02769A633DF37177415C2f8 ) => ( 1966 )
-
IdleCompoundV2.STATICCALL( )
-
AToken.balanceOf( _user=0xF34842d05A1c888Ca02769A633DF37177415C2f8 ) => ( 9752940867595 )
-
IdleAave.STATICCALL( )
-
AdminUpgradeabilityProxy.STATICCALL( )
-
IdleTokenGovernance.DELEGATECALL( )
-
AdminUpgradeabilityProxy.70a08231( )
-
IdleTokenGovernance.balanceOf( account=0x7c91e67910F8DBb947F95f41Ec76dAAc1C2Dc08e ) => ( 2873849150275183502936974 )
-
-
TetherToken.balanceOf( who=0x7c91e67910F8DBb947F95f41Ec76dAAc1C2Dc08e ) => ( 0 )
-
TetherToken.balanceOf( who=0xfd9EeC2Bb8Ed6051Ba557100Ad0Aba87934B51A3 ) => ( 1957266155401 )
-
TetherToken.transferFrom( _from=0x798AfE1a6131EBa2c6C33E70842E3584Fb05DCb0, _to=0xfd9EeC2Bb8Ed6051Ba557100Ad0Aba87934B51A3, _value=1010526273 )
-
deposit[Vault (ln:377)]
_deposit[Vault (ln:378)]
maxDepositCap[Vault (ln:439)]
_maxDepositCap[Vault (ln:198)]
add[Vault (ln:440)]
totalDeposits[Vault (ln:440)]
_totalDeposits[Vault (ln:195)]
maxDepositCap[Vault (ln:440)]
_maxDepositCap[Vault (ln:198)]
strategy[Vault (ln:443)]
_strategy[Vault (ln:180)]
depositArbCheck[Vault (ln:444)]
strategy[Vault (ln:444)]
_strategy[Vault (ln:180)]
totalSupply[Vault (ln:447)]
div[Vault (ln:449)]
mul[Vault (ln:449)]
totalSupply[Vault (ln:449)]
underlyingBalanceWithInvestment[Vault (ln:450)]
strategy[Vault (ln:232)]
_strategy[Vault (ln:180)]
underlyingBalanceInVault[Vault (ln:234)]
balanceOf[Vault (ln:226)]
underlying[Vault (ln:226)]
_underlying[Vault (ln:183)]
add[Vault (ln:237)]
underlyingBalanceInVault[Vault (ln:237)]
balanceOf[Vault (ln:226)]
underlying[Vault (ln:226)]
_underlying[Vault (ln:183)]
investedUnderlyingBalance[Vault (ln:238)]
strategy[Vault (ln:238)]
_strategy[Vault (ln:180)]
_mint[Vault (ln:452)]
safeTransferFrom[Vault (ln:453)]
underlying[Vault (ln:453)]
_underlying[Vault (ln:183)]
_setTotalDeposits[Vault (ln:454)]
add[Vault (ln:454)]
_totalDeposits[Vault (ln:454)]
Deposit[Vault (ln:456)]
File 1 of 11: VaultProxy
File 2 of 11: TetherToken
File 3 of 11: Vault
File 4 of 11: IdleStrategyUSDTMainnet
File 5 of 11: IdleTokenHelper
File 6 of 11: AdminUpgradeabilityProxy
File 7 of 11: IdleTokenGovernance
File 8 of 11: CErc20Delegator
File 9 of 11: IdleCompoundV2
File 10 of 11: AToken
File 11 of 11: IdleAave
pragma solidity 0.5.16; import "./interfaces/IUpgradeSource.sol"; import "@openzeppelin/upgrades/contracts/upgradeability/BaseUpgradeabilityProxy.sol"; contract VaultProxy is BaseUpgradeabilityProxy { constructor(address _implementation) public { _setImplementation(_implementation); } /** * The main logic. If the timer has elapsed and there is a schedule upgrade, * the governance can upgrade the vault */ function upgrade() external { (bool should, address newImplementation) = IUpgradeSource(address(this)).shouldUpgrade(); require(should, "Upgrade not scheduled"); _upgradeTo(newImplementation); // the finalization needs to be executed on itself to update the storage of this proxy // it also needs to be invoked by the governance, not by address(this), so delegatecall is needed (bool success, bytes memory result) = address(this).delegatecall( abi.encodeWithSignature("finalizeUpgrade()") ); require(success, "Issue when finalizing the upgrade"); } function implementation() external view returns (address) { return _implementation(); } } pragma solidity 0.5.16; interface IUpgradeSource { function shouldUpgrade() external view returns (bool, address); function finalizeUpgrade() external; } pragma solidity ^0.5.0; import './Proxy.sol'; import '../utils/Address.sol'; /** * @title BaseUpgradeabilityProxy * @dev This contract implements a proxy that allows to change the * implementation address to which it will delegate. * Such a change is called an implementation upgrade. */ contract BaseUpgradeabilityProxy is Proxy { /** * @dev Emitted when the implementation is upgraded. * @param implementation Address of the new implementation. */ event Upgraded(address indexed implementation); /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Returns the current implementation. * @return Address of the current implementation */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Upgrades the proxy to a new implementation. * @param newImplementation Address of the new implementation. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Sets the implementation address of the proxy. * @param newImplementation Address of the new implementation. */ function _setImplementation(address newImplementation) internal { require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, newImplementation) } } } pragma solidity ^0.5.0; /** * @title Proxy * @dev Implements delegation of calls to other contracts, with proper * forwarding of return values and bubbling of failures. * It defines a fallback function that delegates all calls to the address * returned by the abstract _implementation() internal function. */ contract Proxy { /** * @dev Fallback function. * Implemented entirely in `_fallback`. */ function () payable external { _fallback(); } /** * @return The Address of the implementation. */ function _implementation() internal view returns (address); /** * @dev Delegates execution to an implementation contract. * This is a low level function that doesn't return to its internal call site. * It will return to the external caller whatever the implementation returns. * @param implementation Address to delegate. */ function _delegate(address implementation) internal { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize) } default { return(0, returndatasize) } } } /** * @dev Function that is run as the first thing in the fallback function. * Can be redefined in derived contracts to add functionality. * Redefinitions must call super._willFallback(). */ function _willFallback() internal { } /** * @dev fallback implementation. * Extracted to enable manual triggering. */ function _fallback() internal { _willFallback(); _delegate(_implementation()); } } pragma solidity ^0.5.0; /** * Utility library of inline functions on addresses * * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version. */ library OpenZeppelinUpgradesAddress { /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract, * as the code is not actually created until after the constructor finishes. * @param account address of the account to check * @return whether the target address is a contract */ function isContract(address account) internal view returns (bool) { uint256 size; // XXX Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address. // See https://ethereum.stackexchange.com/a/14016/36603 // for more details about how this works. // TODO Check this again before the Serenity release, because all addresses will be // contracts then. // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } }
File 2 of 11: TetherToken
pragma solidity ^0.4.17; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ function Ownable() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { if (newOwner != address(0)) { owner = newOwner; } } } /** * @title ERC20Basic * @dev Simpler version of ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ contract ERC20Basic { uint public _totalSupply; function totalSupply() public constant returns (uint); function balanceOf(address who) public constant returns (uint); function transfer(address to, uint value) public; event Transfer(address indexed from, address indexed to, uint value); } /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ contract ERC20 is ERC20Basic { function allowance(address owner, address spender) public constant returns (uint); function transferFrom(address from, address to, uint value) public; function approve(address spender, uint value) public; event Approval(address indexed owner, address indexed spender, uint value); } /** * @title Basic token * @dev Basic version of StandardToken, with no allowances. */ contract BasicToken is Ownable, ERC20Basic { using SafeMath for uint; mapping(address => uint) public balances; // additional variables for use if transaction fees ever became necessary uint public basisPointsRate = 0; uint public maximumFee = 0; /** * @dev Fix for the ERC20 short address attack. */ modifier onlyPayloadSize(uint size) { require(!(msg.data.length < size + 4)); _; } /** * @dev transfer token for a specified address * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) { uint fee = (_value.mul(basisPointsRate)).div(10000); if (fee > maximumFee) { fee = maximumFee; } uint sendAmount = _value.sub(fee); balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(sendAmount); if (fee > 0) { balances[owner] = balances[owner].add(fee); Transfer(msg.sender, owner, fee); } Transfer(msg.sender, _to, sendAmount); } /** * @dev Gets the balance of the specified address. * @param _owner The address to query the the balance of. * @return An uint representing the amount owned by the passed address. */ function balanceOf(address _owner) public constant returns (uint balance) { return balances[_owner]; } } /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * @dev https://github.com/ethereum/EIPs/issues/20 * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol */ contract StandardToken is BasicToken, ERC20 { mapping (address => mapping (address => uint)) public allowed; uint public constant MAX_UINT = 2**256 - 1; /** * @dev Transfer tokens from one address to another * @param _from address The address which you want to send tokens from * @param _to address The address which you want to transfer to * @param _value uint the amount of tokens to be transferred */ function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) { var _allowance = allowed[_from][msg.sender]; // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met // if (_value > _allowance) throw; uint fee = (_value.mul(basisPointsRate)).div(10000); if (fee > maximumFee) { fee = maximumFee; } if (_allowance < MAX_UINT) { allowed[_from][msg.sender] = _allowance.sub(_value); } uint sendAmount = _value.sub(fee); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(sendAmount); if (fee > 0) { balances[owner] = balances[owner].add(fee); Transfer(_from, owner, fee); } Transfer(_from, _to, sendAmount); } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * @param _spender The address which will spend the funds. * @param _value The amount of tokens to be spent. */ function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) { // To change the approve amount you first have to reduce the addresses` // allowance to zero by calling `approve(_spender, 0)` if it is not // already 0 to mitigate the race condition described here: // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 require(!((_value != 0) && (allowed[msg.sender][_spender] != 0))); allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); } /** * @dev Function to check the amount of tokens than an owner allowed to a spender. * @param _owner address The address which owns the funds. * @param _spender address The address which will spend the funds. * @return A uint specifying the amount of tokens still available for the spender. */ function allowance(address _owner, address _spender) public constant returns (uint remaining) { return allowed[_owner][_spender]; } } /** * @title Pausable * @dev Base contract which allows children to implement an emergency stop mechanism. */ contract Pausable is Ownable { event Pause(); event Unpause(); bool public paused = false; /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!paused); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() onlyOwner whenNotPaused public { paused = true; Pause(); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() onlyOwner whenPaused public { paused = false; Unpause(); } } contract BlackList is Ownable, BasicToken { /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) /////// function getBlackListStatus(address _maker) external constant returns (bool) { return isBlackListed[_maker]; } function getOwner() external constant returns (address) { return owner; } mapping (address => bool) public isBlackListed; function addBlackList (address _evilUser) public onlyOwner { isBlackListed[_evilUser] = true; AddedBlackList(_evilUser); } function removeBlackList (address _clearedUser) public onlyOwner { isBlackListed[_clearedUser] = false; RemovedBlackList(_clearedUser); } function destroyBlackFunds (address _blackListedUser) public onlyOwner { require(isBlackListed[_blackListedUser]); uint dirtyFunds = balanceOf(_blackListedUser); balances[_blackListedUser] = 0; _totalSupply -= dirtyFunds; DestroyedBlackFunds(_blackListedUser, dirtyFunds); } event DestroyedBlackFunds(address _blackListedUser, uint _balance); event AddedBlackList(address _user); event RemovedBlackList(address _user); } contract UpgradedStandardToken is StandardToken{ // those methods are called by the legacy contract // and they must ensure msg.sender to be the contract address function transferByLegacy(address from, address to, uint value) public; function transferFromByLegacy(address sender, address from, address spender, uint value) public; function approveByLegacy(address from, address spender, uint value) public; } contract TetherToken is Pausable, StandardToken, BlackList { string public name; string public symbol; uint public decimals; address public upgradedAddress; bool public deprecated; // The contract can be initialized with a number of tokens // All the tokens are deposited to the owner address // // @param _balance Initial supply of the contract // @param _name Token Name // @param _symbol Token symbol // @param _decimals Token decimals function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public { _totalSupply = _initialSupply; name = _name; symbol = _symbol; decimals = _decimals; balances[owner] = _initialSupply; deprecated = false; } // Forward ERC20 methods to upgraded contract if this one is deprecated function transfer(address _to, uint _value) public whenNotPaused { require(!isBlackListed[msg.sender]); if (deprecated) { return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value); } else { return super.transfer(_to, _value); } } // Forward ERC20 methods to upgraded contract if this one is deprecated function transferFrom(address _from, address _to, uint _value) public whenNotPaused { require(!isBlackListed[_from]); if (deprecated) { return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value); } else { return super.transferFrom(_from, _to, _value); } } // Forward ERC20 methods to upgraded contract if this one is deprecated function balanceOf(address who) public constant returns (uint) { if (deprecated) { return UpgradedStandardToken(upgradedAddress).balanceOf(who); } else { return super.balanceOf(who); } } // Forward ERC20 methods to upgraded contract if this one is deprecated function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) { if (deprecated) { return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value); } else { return super.approve(_spender, _value); } } // Forward ERC20 methods to upgraded contract if this one is deprecated function allowance(address _owner, address _spender) public constant returns (uint remaining) { if (deprecated) { return StandardToken(upgradedAddress).allowance(_owner, _spender); } else { return super.allowance(_owner, _spender); } } // deprecate current contract in favour of a new one function deprecate(address _upgradedAddress) public onlyOwner { deprecated = true; upgradedAddress = _upgradedAddress; Deprecate(_upgradedAddress); } // deprecate current contract if favour of a new one function totalSupply() public constant returns (uint) { if (deprecated) { return StandardToken(upgradedAddress).totalSupply(); } else { return _totalSupply; } } // Issue a new amount of tokens // these tokens are deposited into the owner address // // @param _amount Number of tokens to be issued function issue(uint amount) public onlyOwner { require(_totalSupply + amount > _totalSupply); require(balances[owner] + amount > balances[owner]); balances[owner] += amount; _totalSupply += amount; Issue(amount); } // Redeem tokens. // These tokens are withdrawn from the owner address // if the balance must be enough to cover the redeem // or the call will fail. // @param _amount Number of tokens to be issued function redeem(uint amount) public onlyOwner { require(_totalSupply >= amount); require(balances[owner] >= amount); _totalSupply -= amount; balances[owner] -= amount; Redeem(amount); } function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner { // Ensure transparency by hardcoding limit beyond which fees can never be added require(newBasisPoints < 20); require(newMaxFee < 50); basisPointsRate = newBasisPoints; maximumFee = newMaxFee.mul(10**decimals); Params(basisPointsRate, maximumFee); } // Called when new token are issued event Issue(uint amount); // Called when tokens are redeemed event Redeem(uint amount); // Called when contract is deprecated event Deprecate(address newAddress); // Called if contract ever adds fees event Params(uint feeBasisPoints, uint maxFee); }
File 3 of 11: Vault
pragma solidity 0.5.16; import "./GovernableInit.sol"; // A clone of Governable supporting the Initializable interface and pattern contract ControllableInit is GovernableInit { constructor() public {} function initialize(address _storage) public initializer { GovernableInit.initialize(_storage); } modifier onlyController() { require( Storage(_storage()).isController(msg.sender), "Not a controller" ); _; } modifier onlyControllerOrGovernance() { require( (Storage(_storage()).isController(msg.sender) || Storage(_storage()).isGovernance(msg.sender)), "The caller must be controller or governance" ); _; } function controller() public view returns (address) { return Storage(_storage()).controller(); } } pragma solidity 0.5.16; import "@openzeppelin/upgrades/contracts/Initializable.sol"; import "./Storage.sol"; // A clone of Governable supporting the Initializable interface and pattern contract GovernableInit is Initializable { bytes32 internal constant _STORAGE_SLOT = 0xa7ec62784904ff31cbcc32d09932a58e7f1e4476e1d041995b37c917990b16dc; modifier onlyGovernance() { require(Storage(_storage()).isGovernance(msg.sender), "Not governance"); _; } constructor() public { assert( _STORAGE_SLOT == bytes32( uint256(keccak256("eip1967.governableInit.storage")) - 1 ) ); } function initialize(address _store) public initializer { _setStorage(_store); } function _setStorage(address newStorage) private { bytes32 slot = _STORAGE_SLOT; // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, newStorage) } } function setStorage(address _store) public onlyGovernance { require(_store != address(0), "new storage shouldn't be empty"); _setStorage(_store); } function _storage() internal view returns (address str) { bytes32 slot = _STORAGE_SLOT; // solhint-disable-next-line no-inline-assembly assembly { str := sload(slot) } } function governance() public view returns (address) { return Storage(_storage()).governance(); } } pragma solidity 0.5.16; contract Storage { address public governance; address public controller; constructor() public { governance = msg.sender; } modifier onlyGovernance() { require(isGovernance(msg.sender), "Not governance"); _; } function setGovernance(address _governance) public onlyGovernance { require(_governance != address(0), "new governance shouldn't be empty"); governance = _governance; } function setController(address _controller) public onlyGovernance { require(_controller != address(0), "new controller shouldn't be empty"); controller = _controller; } function isGovernance(address account) public view returns (bool) { return account == governance; } function isController(address account) public view returns (bool) { return account == controller; } } pragma solidity 0.5.16; import "@openzeppelin/contracts-ethereum-package/contracts/math/Math.sol"; import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol"; import "@openzeppelin/contracts-ethereum-package/contracts/utils/ReentrancyGuard.sol"; import "./interfaces/IStrategy.sol"; import "./interfaces/IController.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IUpgradeSource.sol"; import "./ControllableInit.sol"; import "./VaultStorage.sol"; contract Vault is ERC20, ERC20Detailed, IVault, IUpgradeSource, ControllableInit, VaultStorage, ReentrancyGuard { using SafeERC20 for IERC20; using Address for address; using SafeMath for uint256; event Withdraw(address indexed beneficiary, uint256 amount); event Deposit(address indexed beneficiary, uint256 amount); event Invest(uint256 amount); event StrategyAnnounced(address newStrategy, uint256 time); event StrategyChanged(address newStrategy, address oldStrategy); modifier whenStrategyDefined() { require(address(strategy()) != address(0), "Strategy must be defined"); _; } // Only smart contracts will be affected by this modifier modifier defense() { require( (msg.sender == tx.origin) || // If it is a normal user and not smart contract, // then the requirement will pass IController(controller()).whiteList(msg.sender), "Access denied for caller" ); _; } // the function is name differently to not cause inheritance clash in truffle and allows tests function initializeVault( address _storage, address _underlying, uint256 _toInvestNumerator, uint256 _toInvestDenominator, uint256 _maxDepositCap ) public initializer { require( _toInvestNumerator <= _toInvestDenominator, "cannot invest more than 100%" ); require(_toInvestDenominator != 0, "cannot divide by 0"); ERC20Detailed.initialize( string( abi.encodePacked("FORCE_", ERC20Detailed(_underlying).symbol()) ), string(abi.encodePacked("x", ERC20Detailed(_underlying).symbol())), ERC20Detailed(_underlying).decimals() ); ControllableInit.initialize(_storage); ReentrancyGuard.initialize(); uint256 underlyingUnit = 10**uint256(ERC20Detailed(address(_underlying)).decimals()); uint256 implementationDelay = 12 hours; uint256 strategyChangeDelay = 12 hours; VaultStorage.initialize( _underlying, _toInvestNumerator, _toInvestDenominator, underlyingUnit, _maxDepositCap, implementationDelay, strategyChangeDelay ); } function strategy() public view returns (address) { return _strategy(); } function underlying() public view returns (address) { return _underlying(); } function underlyingUnit() public view returns (uint256) { return _underlyingUnit(); } function vaultFractionToInvestNumerator() public view returns (uint256) { return _vaultFractionToInvestNumerator(); } function vaultFractionToInvestDenominator() public view returns (uint256) { return _vaultFractionToInvestDenominator(); } function totalDeposits() public view returns (uint256) { return _totalDeposits(); } function maxDepositCap() public view returns (uint256) { return _maxDepositCap(); } function nextImplementation() public view returns (address) { return _nextImplementation(); } function nextImplementationTimestamp() public view returns (uint256) { return _nextImplementationTimestamp(); } function nextImplementationDelay() public view returns (uint256) { return _nextImplementationDelay(); } /** * Chooses the best strategy and re-invests. If the strategy did not change, it just calls * forceUnleashed on the current strategy. Call this through controller to claim galactic rewards. */ function forceUnleashed() external whenStrategyDefined onlyControllerOrGovernance { // ensure that new funds are invested too invest(); IStrategy(strategy()).forceUnleashed(); } /* * Returns the cash balance across all users in this contract. */ function underlyingBalanceInVault() public view returns (uint256) { return IERC20(underlying()).balanceOf(address(this)); } /* Returns the current underlying (e.g., DAI's) balance together with * the invested amount (if DAI is invested elsewhere by the strategy). */ function underlyingBalanceWithInvestment() public view returns (uint256) { if (address(strategy()) == address(0)) { // initial state, when not set return underlyingBalanceInVault(); } return underlyingBalanceInVault().add( IStrategy(strategy()).investedUnderlyingBalance() ); } function getPricePerFullShare() public view returns (uint256) { return totalSupply() == 0 ? underlyingUnit() : underlyingUnit().mul(underlyingBalanceWithInvestment()).div( totalSupply() ); } /* get the user's share (in underlying) */ function underlyingBalanceWithInvestmentForHolder(address holder) external view returns (uint256) { if (totalSupply() == 0) { return 0; } return underlyingBalanceWithInvestment().mul(balanceOf(holder)).div( totalSupply() ); } function futureStrategy() public view returns (address) { return _futureStrategy(); } function strategyUpdateTime() public view returns (uint256) { return _strategyUpdateTime(); } function strategyTimeLock() public view returns (uint256) { return _strategyTimeLock(); } function canUpdateStrategy(address _strategy) public view returns (bool) { return strategy() == address(0) || // no strategy was set yet (_strategy == futureStrategy() && block.timestamp > strategyUpdateTime() && strategyUpdateTime() > 0); // or the timelock has passed } /** * Indicates that the strategy update will happen in the future */ function announceStrategyUpdate(address _strategy) public onlyControllerOrGovernance { // records a new timestamp uint256 when = block.timestamp.add(strategyTimeLock()); _setStrategyUpdateTime(when); _setFutureStrategy(_strategy); emit StrategyAnnounced(_strategy, when); } /** * Finalizes (or cancels) the strategy update by resetting the data */ function finalizeStrategyUpdate() public onlyControllerOrGovernance { _setStrategyUpdateTime(0); _setFutureStrategy(address(0)); } function setStrategy(address _strategy) public onlyControllerOrGovernance { require( canUpdateStrategy(_strategy), "The strategy exists and switch timelock did not elapse yet" ); require(_strategy != address(0), "new _strategy cannot be empty"); require( IStrategy(_strategy).underlying() == address(underlying()), "Vault underlying must match Strategy underlying" ); require( IStrategy(_strategy).vault() == address(this), "the strategy does not belong to this vault" ); emit StrategyChanged(_strategy, strategy()); if (address(_strategy) != address(strategy())) { if (address(strategy()) != address(0)) { // if the original strategy (no underscore) is defined IERC20(underlying()).safeApprove(address(strategy()), 0); IStrategy(strategy()).withdrawAllToVault(); } _setStrategy(_strategy); IERC20(underlying()).safeApprove(address(strategy()), 0); IERC20(underlying()).safeApprove(address(strategy()), uint256(~0)); } finalizeStrategyUpdate(); } function setVaultFractionToInvest(uint256 numerator, uint256 denominator) external onlyGovernance { require(denominator > 0, "denominator must be greater than 0"); require( numerator <= denominator, "denominator must be greater than or equal to the numerator" ); _setVaultFractionToInvestNumerator(numerator); _setVaultFractionToInvestDenominator(denominator); } function setMaxDepositCap(uint256 value) external onlyGovernance { return _setMaxDepositCap(value); } function rebalance() external onlyControllerOrGovernance { withdrawAll(); invest(); } function availableToInvestOut() public view returns (uint256) { uint256 wantInvestInTotal = underlyingBalanceWithInvestment() .mul(vaultFractionToInvestNumerator()) .div(vaultFractionToInvestDenominator()); uint256 alreadyInvested = IStrategy(strategy()).investedUnderlyingBalance(); if (alreadyInvested >= wantInvestInTotal) { return 0; } else { uint256 remainingToInvest = wantInvestInTotal.sub(alreadyInvested); return remainingToInvest <= underlyingBalanceInVault() // TODO: we think that the "else" branch of the ternary operation is not // going to get hit ? remainingToInvest : underlyingBalanceInVault(); } } function invest() internal whenStrategyDefined { uint256 availableAmount = availableToInvestOut(); if (availableAmount > 0) { IERC20(underlying()).safeTransfer( address(strategy()), availableAmount ); emit Invest(availableAmount); } } /* * Allows for depositing the underlying asset in exchange for shares. * Approval is assumed. */ function deposit(uint256 amount) external defense { _deposit(amount, msg.sender, msg.sender); } /* * Allows for depositing the underlying asset in exchange for shares * assigned to the holder. * This facilitates depositing for someone else (using DepositHelper) */ function depositFor(uint256 amount, address holder) public defense { _deposit(amount, msg.sender, holder); } function withdrawAll() public onlyControllerOrGovernance whenStrategyDefined { IStrategy(strategy()).withdrawAllToVault(); } function withdraw(uint256 numberOfShares) external defense nonReentrant { require(totalSupply() > 0, "Vault has no shares"); require(numberOfShares > 0, "numberOfShares must be greater than 0"); uint256 totalSupply = totalSupply(); _burn(msg.sender, numberOfShares); uint256 underlyingAmountToWithdraw = underlyingBalanceWithInvestment().mul(numberOfShares).div( totalSupply ); uint256 originalDepositsToWithdraw = _totalDeposits().mul(numberOfShares).div(totalSupply); if (underlyingAmountToWithdraw > underlyingBalanceInVault()) { // withdraw everything from the strategy to accurately check the share value if (numberOfShares == totalSupply) { IStrategy(strategy()).withdrawAllToVault(); } else { uint256 missing = underlyingAmountToWithdraw.sub(underlyingBalanceInVault()); IStrategy(strategy()).withdrawToVault(missing); } // recalculate to improve accuracy underlyingAmountToWithdraw = Math.min( underlyingBalanceWithInvestment().mul(numberOfShares).div( totalSupply ), underlyingBalanceInVault() ); } IERC20(underlying()).safeTransfer( msg.sender, underlyingAmountToWithdraw ); _setTotalDeposits(_totalDeposits().sub(originalDepositsToWithdraw)); // update the withdrawal amount for the holder emit Withdraw(msg.sender, underlyingAmountToWithdraw); } function _deposit( uint256 amount, address sender, address beneficiary ) internal nonReentrant { require(amount > 0, "Cannot deposit 0"); require(beneficiary != address(0), "holder must be defined"); require( maxDepositCap() == 0 || totalDeposits().add(amount) <= maxDepositCap(), "Cannot deposit more than cap" ); if (address(strategy()) != address(0)) { require(IStrategy(strategy()).depositArbCheck(), "Too much arb"); } uint256 toMint = totalSupply() == 0 ? amount : amount.mul(totalSupply()).div( underlyingBalanceWithInvestment() ); _mint(beneficiary, toMint); IERC20(underlying()).safeTransferFrom(sender, address(this), amount); _setTotalDeposits(_totalDeposits().add(amount)); // update the contribution amount for the beneficiary emit Deposit(beneficiary, amount); } /** * Schedules an upgrade for this vault's proxy. */ function scheduleUpgrade(address impl) public onlyGovernance { _setNextImplementation(impl); _setNextImplementationTimestamp( block.timestamp.add(nextImplementationDelay()) ); } function shouldUpgrade() external view returns (bool, address) { return ( nextImplementationTimestamp() != 0 && block.timestamp > nextImplementationTimestamp() && nextImplementation() != address(0), nextImplementation() ); } function finalizeUpgrade() external onlyGovernance { _setNextImplementation(address(0)); _setNextImplementationTimestamp(0); } } pragma solidity 0.5.16; import "@openzeppelin/upgrades/contracts/Initializable.sol"; contract VaultStorage is Initializable { bytes32 internal constant _STRATEGY_SLOT = 0xf1a169aa0f736c2813818fdfbdc5755c31e0839c8f49831a16543496b28574ea; bytes32 internal constant _UNDERLYING_SLOT = 0x1994607607e11d53306ef62e45e3bd85762c58d9bf38b5578bc4a258a26a7371; bytes32 internal constant _UNDERLYING_UNIT_SLOT = 0xa66bc57d4b4eed7c7687876ca77997588987307cb13ecc23f5e52725192e5fff; bytes32 internal constant _VAULT_FRACTION_TO_INVEST_NUMERATOR_SLOT = 0x39122c9adfb653455d0c05043bd52fcfbc2be864e832efd3abc72ce5a3d7ed5a; bytes32 internal constant _VAULT_FRACTION_TO_INVEST_DENOMINATOR_SLOT = 0x469a3bad2fab7b936c45eecd1f5da52af89cead3e2ed7f732b6f3fc92ed32308; bytes32 internal constant _NEXT_IMPLEMENTATION_SLOT = 0xb1acf527cd7cd1668b30e5a9a1c0d845714604de29ce560150922c9d8c0937df; bytes32 internal constant _NEXT_IMPLEMENTATION_TIMESTAMP_SLOT = 0x3bc747f4b148b37be485de3223c90b4468252967d2ea7f9fcbd8b6e653f434c9; bytes32 internal constant _NEXT_IMPLEMENTATION_DELAY_SLOT = 0x82ddc3be3f0c1a6870327f78f4979a0b37b21b16736ef5be6a7a7a35e530bcf0; bytes32 internal constant _STRATEGY_TIME_LOCK_SLOT = 0x6d02338b2e4c913c0f7d380e2798409838a48a2c4d57d52742a808c82d713d8b; bytes32 internal constant _FUTURE_STRATEGY_SLOT = 0xb441b53a4e42c2ca9182bc7ede99bedba7a5d9360d9dfbd31fa8ee2dc8590610; bytes32 internal constant _STRATEGY_UPDATE_TIME_SLOT = 0x56e7c0e75875c6497f0de657009613a32558904b5c10771a825cc330feff7e72; bytes32 internal constant _TOTAL_DEPOSITS = 0xaf765835ed5af0d235b6c686724ad31fa90e06b3daf1c074d6cc398b8fcef213; bytes32 internal constant _MAX_DEPOSIT_CAP = 0x0df75d4bdb87be8e3e04e1dc08ec1c98ed6c4147138e5789f0bd448c5c8e1e28; constructor() public { assert( _STRATEGY_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.strategy")) - 1) ); assert( _UNDERLYING_SLOT == bytes32( uint256(keccak256("eip1967.vaultStorage.underlying")) - 1 ) ); assert( _UNDERLYING_UNIT_SLOT == bytes32( uint256(keccak256("eip1967.vaultStorage.underlyingUnit")) - 1 ) ); assert( _VAULT_FRACTION_TO_INVEST_NUMERATOR_SLOT == bytes32( uint256( keccak256( "eip1967.vaultStorage.vaultFractionToInvestNumerator" ) ) - 1 ) ); assert( _VAULT_FRACTION_TO_INVEST_DENOMINATOR_SLOT == bytes32( uint256( keccak256( "eip1967.vaultStorage.vaultFractionToInvestDenominator" ) ) - 1 ) ); assert( _NEXT_IMPLEMENTATION_SLOT == bytes32( uint256( keccak256("eip1967.vaultStorage.nextImplementation") ) - 1 ) ); assert( _NEXT_IMPLEMENTATION_TIMESTAMP_SLOT == bytes32( uint256( keccak256( "eip1967.vaultStorage.nextImplementationTimestamp" ) ) - 1 ) ); assert( _NEXT_IMPLEMENTATION_DELAY_SLOT == bytes32( uint256( keccak256( "eip1967.vaultStorage.nextImplementationDelay" ) ) - 1 ) ); assert( _STRATEGY_TIME_LOCK_SLOT == bytes32( uint256( keccak256("eip1967.vaultStorage.strategyTimeLock") ) - 1 ) ); assert( _FUTURE_STRATEGY_SLOT == bytes32( uint256(keccak256("eip1967.vaultStorage.futureStrategy")) - 1 ) ); assert( _STRATEGY_UPDATE_TIME_SLOT == bytes32( uint256( keccak256("eip1967.vaultStorage.strategyUpdateTime") ) - 1 ) ); assert( _TOTAL_DEPOSITS == bytes32( uint256(keccak256("eip1967.vaultStorage.totalDeposits")) - 1 ) ); assert( _MAX_DEPOSIT_CAP == bytes32( uint256(keccak256("eip1967.vaultStorage.maxDepositCap")) - 1 ) ); } function initialize( address _underlying, uint256 _toInvestNumerator, uint256 _toInvestDenominator, uint256 _underlyingUnit, uint256 _maxDepositCap_, uint256 _implementationChangeDelay, uint256 _strategyChangeDelay ) public initializer { _setUnderlying(_underlying); _setVaultFractionToInvestNumerator(_toInvestNumerator); _setVaultFractionToInvestDenominator(_toInvestDenominator); _setMaxDepositCap(_maxDepositCap_); _setUnderlyingUnit(_underlyingUnit); _setNextImplementationDelay(_implementationChangeDelay); _setStrategyTimeLock(_strategyChangeDelay); _setStrategyUpdateTime(0); _setFutureStrategy(address(0)); } function _setStrategy(address _address) internal { setAddress(_STRATEGY_SLOT, _address); } function _strategy() internal view returns (address) { return getAddress(_STRATEGY_SLOT); } function _setUnderlying(address _address) internal { setAddress(_UNDERLYING_SLOT, _address); } function _underlying() internal view returns (address) { return getAddress(_UNDERLYING_SLOT); } function _setUnderlyingUnit(uint256 _value) internal { setUint256(_UNDERLYING_UNIT_SLOT, _value); } function _underlyingUnit() internal view returns (uint256) { return getUint256(_UNDERLYING_UNIT_SLOT); } function _setVaultFractionToInvestNumerator(uint256 _value) internal { setUint256(_VAULT_FRACTION_TO_INVEST_NUMERATOR_SLOT, _value); } function _vaultFractionToInvestNumerator() internal view returns (uint256) { return getUint256(_VAULT_FRACTION_TO_INVEST_NUMERATOR_SLOT); } function _setVaultFractionToInvestDenominator(uint256 _value) internal { setUint256(_VAULT_FRACTION_TO_INVEST_DENOMINATOR_SLOT, _value); } function _vaultFractionToInvestDenominator() internal view returns (uint256) { return getUint256(_VAULT_FRACTION_TO_INVEST_DENOMINATOR_SLOT); } function _setTotalDeposits(uint256 _value) internal { setUint256(_TOTAL_DEPOSITS, _value); } function _totalDeposits() internal view returns (uint256) { return getUint256(_TOTAL_DEPOSITS); } function _setMaxDepositCap(uint256 _value) internal { setUint256(_MAX_DEPOSIT_CAP, _value); } function _maxDepositCap() internal view returns (uint256) { return getUint256(_MAX_DEPOSIT_CAP); } function _setNextImplementation(address _address) internal { setAddress(_NEXT_IMPLEMENTATION_SLOT, _address); } function _nextImplementation() internal view returns (address) { return getAddress(_NEXT_IMPLEMENTATION_SLOT); } function _setNextImplementationTimestamp(uint256 _value) internal { setUint256(_NEXT_IMPLEMENTATION_TIMESTAMP_SLOT, _value); } function _nextImplementationTimestamp() internal view returns (uint256) { return getUint256(_NEXT_IMPLEMENTATION_TIMESTAMP_SLOT); } function _setNextImplementationDelay(uint256 _value) internal { setUint256(_NEXT_IMPLEMENTATION_DELAY_SLOT, _value); } function _nextImplementationDelay() internal view returns (uint256) { return getUint256(_NEXT_IMPLEMENTATION_DELAY_SLOT); } function _setStrategyTimeLock(uint256 _value) internal { setUint256(_STRATEGY_TIME_LOCK_SLOT, _value); } function _strategyTimeLock() internal view returns (uint256) { return getUint256(_STRATEGY_TIME_LOCK_SLOT); } function _setFutureStrategy(address _value) internal { setAddress(_FUTURE_STRATEGY_SLOT, _value); } function _futureStrategy() internal view returns (address) { return getAddress(_FUTURE_STRATEGY_SLOT); } function _setStrategyUpdateTime(uint256 _value) internal { setUint256(_STRATEGY_UPDATE_TIME_SLOT, _value); } function _strategyUpdateTime() internal view returns (uint256) { return getUint256(_STRATEGY_UPDATE_TIME_SLOT); } function setAddress(bytes32 slot, address _address) private { // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, _address) } } function setUint256(bytes32 slot, uint256 _value) private { // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, _value) } } function getAddress(bytes32 slot) private view returns (address str) { // solhint-disable-next-line no-inline-assembly assembly { str := sload(slot) } } function getUint256(bytes32 slot) private view returns (uint256 str) { // solhint-disable-next-line no-inline-assembly assembly { str := sload(slot) } } uint256[50] private ______gap; } pragma solidity 0.5.16; interface IController { function whiteList(address _target) external view returns (bool); function addVaultAndStrategy(address _vault, address _strategy) external; function forceUnleashed(address _vault) external; function hasVault(address _vault) external returns (bool); function salvage(address _token, uint256 amount) external; function salvageStrategy( address _strategy, address _token, uint256 amount ) external; function notifyFee(address _underlying, uint256 fee) external; function profitSharingNumerator() external view returns (uint256); function profitSharingDenominator() external view returns (uint256); function treasury() external view returns (address); } pragma solidity 0.5.16; interface IStrategy { function unsalvagableTokens(address tokens) external view returns (bool); function governance() external view returns (address); function controller() external view returns (address); function underlying() external view returns (address); function vault() external view returns (address); function withdrawAllToVault() external; function withdrawToVault(uint256 amount) external; function investedUnderlyingBalance() external view returns (uint256); // itsNotMuch() // should only be called by controller function salvage( address recipient, address token, uint256 amount ) external; function forceUnleashed() external; function depositArbCheck() external view returns (bool); } pragma solidity 0.5.16; interface IUpgradeSource { function shouldUpgrade() external view returns (bool, address); function finalizeUpgrade() external; } pragma solidity 0.5.16; interface IVault { function underlyingBalanceInVault() external view returns (uint256); function underlyingBalanceWithInvestment() external view returns (uint256); function governance() external view returns (address); function controller() external view returns (address); function underlying() external view returns (address); function strategy() external view returns (address); function setStrategy(address _strategy) external; function setVaultFractionToInvest(uint256 numerator, uint256 denominator) external; function deposit(uint256 amountWei) external; function depositFor(uint256 amountWei, address holder) external; function withdrawAll() external; function withdraw(uint256 numberOfShares) external; function getPricePerFullShare() external view returns (uint256); function underlyingBalanceWithInvestmentForHolder(address holder) external view returns (uint256); // force unleash should be callable only by the controller (by the force unleasher) or by governance function forceUnleashed() external; function rebalance() external; } pragma solidity ^0.5.0; import "@openzeppelin/upgrades/contracts/Initializable.sol"; /* * @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. */ contract Context is Initializable { // Empty internal constructor, to prevent people from mistakenly deploying // an instance of this contract, which should be used via inheritance. constructor () internal { } // solhint-disable-previous-line no-empty-blocks function _msgSender() internal view returns (address payable) { return msg.sender; } function _msgData() internal view returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } pragma solidity ^0.5.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } } pragma solidity ^0.5.0; /** * @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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } pragma solidity ^0.5.0; import "@openzeppelin/upgrades/contracts/Initializable.sol"; import "../../GSN/Context.sol"; import "./IERC20.sol"; import "../../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 {ERC20Mintable}. * * 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 Initializable, Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public 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`. */ function transfer(address recipient, uint256 amount) public returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public 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`. */ function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @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. */ function increaseAllowance(address spender, uint256 addedValue) public 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`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); 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, uint256 amount) internal { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); 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 { require(account != address(0), "ERC20: mint to the zero address"); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); 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 { require(account != address(0), "ERC20: burn from the zero address"); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); 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, uint256 amount) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Destroys `amount` tokens from `account`.`amount` is then deducted * from the caller's allowance. * * See {_burn} and {_approve}. */ function _burnFrom(address account, uint256 amount) internal { _burn(account, amount); _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); } uint256[50] private ______gap; } pragma solidity ^0.5.0; import "@openzeppelin/upgrades/contracts/Initializable.sol"; import "./IERC20.sol"; /** * @dev Optional functions from the ERC20 standard. */ contract ERC20Detailed is Initializable, IERC20 { string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of * these values are immutable: they can only be set once during * construction. */ function initialize(string memory name, string memory symbol, uint8 decimals) public initializer { _name = name; _symbol = symbol; _decimals = decimals; } /** * @dev Returns the name of the token. */ function name() public 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 _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. * * 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 _decimals; } uint256[50] private ______gap; } pragma solidity ^0.5.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see {ERC20Detailed}. */ 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); } pragma solidity ^0.5.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/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 ERC20;` 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 { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length 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 safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length 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"); } } } pragma solidity ^0.5.5; /** * @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 Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * _Available since v2.4.0._ */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } /** * @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]. * * _Available since v2.4.0._ */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-call-value (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } pragma solidity ^0.5.0; import "@openzeppelin/upgrades/contracts/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. */ contract ReentrancyGuard is Initializable { // counter to allow mutex lock with only one SSTORE operation uint256 private _guardCounter; function initialize() public initializer { // The counter starts at one to prevent changing it from zero to a non-zero // value, which is a more expensive operation. _guardCounter = 1; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { _guardCounter += 1; uint256 localCounter = _guardCounter; _; require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call"); } uint256[50] private ______gap; } pragma solidity >=0.4.24 <0.7.0; /** * @title Initializable * * @dev Helper contract to support initializer functions. To use it, replace * the constructor with a function that has the `initializer` modifier. * WARNING: Unlike constructors, initializer functions must be manually * invoked. This applies both to deploying an Initializable contract, as well * as extending an Initializable contract via inheritance. * WARNING: When used with inheritance, manual care must be taken to not invoke * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. */ contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private initializing; /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; initialized = true; } _; if (isTopLevelCall) { initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function isConstructor() private view returns (bool) { // extcodesize checks the size of the code stored in an address, and // address returns the current address. Since the code is still not // deployed when running a constructor, any checks on its code size will // yield zero, making it an effective way to detect if a contract is // under construction or not. address self = address(this); uint256 cs; assembly { cs := extcodesize(self) } return cs == 0; } // Reserved storage space to allow for layout changes in the future. uint256[50] private ______gap; }
File 4 of 11: IdleStrategyUSDTMainnet
pragma solidity 0.5.16; import "./Governable.sol"; contract Controllable is Governable { constructor(address _storage) public Governable(_storage) {} modifier onlyController() { require(store.isController(msg.sender), "Not a controller"); _; } modifier onlyControllerOrGovernance() { require( (store.isController(msg.sender) || store.isGovernance(msg.sender)), "The caller must be controller or governance" ); _; } function controller() public view returns (address) { return store.controller(); } } pragma solidity 0.5.16; import "./Storage.sol"; contract Governable { Storage public store; constructor(address _store) public { require(_store != address(0), "new storage shouldn't be empty"); store = Storage(_store); } modifier onlyGovernance() { require(store.isGovernance(msg.sender), "Not governance"); _; } function setStorage(address _store) public onlyGovernance { require(_store != address(0), "new storage shouldn't be empty"); store = Storage(_store); } function governance() public view returns (address) { return store.governance(); } } pragma solidity 0.5.16; contract Storage { address public governance; address public controller; constructor() public { governance = msg.sender; } modifier onlyGovernance() { require(isGovernance(msg.sender), "Not governance"); _; } function setGovernance(address _governance) public onlyGovernance { require(_governance != address(0), "new governance shouldn't be empty"); governance = _governance; } function setController(address _controller) public onlyGovernance { require(_controller != address(0), "new controller shouldn't be empty"); controller = _controller; } function isGovernance(address account) public view returns (bool) { return account == governance; } function isController(address account) public view returns (bool) { return account == controller; } } pragma solidity 0.5.16; interface IController { function whiteList(address _target) external view returns (bool); function addVaultAndStrategy(address _vault, address _strategy) external; function forceUnleashed(address _vault) external; function hasVault(address _vault) external returns (bool); function salvage(address _token, uint256 amount) external; function salvageStrategy( address _strategy, address _token, uint256 amount ) external; function notifyFee(address _underlying, uint256 fee) external; function profitSharingNumerator() external view returns (uint256); function profitSharingDenominator() external view returns (uint256); function treasury() external view returns (address); } pragma solidity 0.5.16; interface IStrategy { function unsalvagableTokens(address tokens) external view returns (bool); function governance() external view returns (address); function controller() external view returns (address); function underlying() external view returns (address); function vault() external view returns (address); function withdrawAllToVault() external; function withdrawToVault(uint256 amount) external; function investedUnderlyingBalance() external view returns (uint256); // itsNotMuch() // should only be called by controller function salvage( address recipient, address token, uint256 amount ) external; function forceUnleashed() external; function depositArbCheck() external view returns (bool); } pragma solidity 0.5.16; interface IVault { function underlyingBalanceInVault() external view returns (uint256); function underlyingBalanceWithInvestment() external view returns (uint256); function governance() external view returns (address); function controller() external view returns (address); function underlying() external view returns (address); function strategy() external view returns (address); function setStrategy(address _strategy) external; function setVaultFractionToInvest(uint256 numerator, uint256 denominator) external; function deposit(uint256 amountWei) external; function depositFor(uint256 amountWei, address holder) external; function withdrawAll() external; function withdraw(uint256 numberOfShares) external; function getPricePerFullShare() external view returns (uint256); function underlyingBalanceWithInvestmentForHolder(address holder) external view returns (uint256); // force unleash should be callable only by the controller (by the force unleasher) or by governance function forceUnleashed() external; function rebalance() external; } pragma solidity 0.5.16; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../interfaces/IController.sol"; import "../Controllable.sol"; contract RewardTokenProfitNotifier is Controllable { using SafeMath for uint256; using SafeERC20 for IERC20; uint256 public profitSharingNumerator; uint256 public profitSharingDenominator; address public rewardToken; constructor(address _storage, address _rewardToken) public Controllable(_storage) { rewardToken = _rewardToken; profitSharingNumerator = 0; profitSharingDenominator = 100; require( profitSharingNumerator < profitSharingDenominator, "invalid profit share" ); } event ProfitLogInReward( uint256 profitAmount, uint256 feeAmount, uint256 timestamp ); function notifyProfitInRewardToken(uint256 _rewardBalance) internal { if (_rewardBalance > 0 && profitSharingNumerator > 0) { uint256 feeAmount = _rewardBalance.mul(profitSharingNumerator).div( profitSharingDenominator ); emit ProfitLogInReward(_rewardBalance, feeAmount, block.timestamp); IERC20(rewardToken).safeApprove(controller(), 0); IERC20(rewardToken).safeApprove(controller(), feeAmount); IController(controller()).notifyFee(rewardToken, feeAmount); } else { emit ProfitLogInReward(0, 0, block.timestamp); } } function setProfitSharingNumerator(uint256 _profitSharingNumerator) external onlyGovernance { profitSharingNumerator = _profitSharingNumerator; } } pragma solidity 0.5.16; contract IIdleTokenHelper { function getMintingPrice(address idleYieldToken) external view returns (uint256 mintingPrice); function getRedeemPrice(address idleYieldToken) external view returns (uint256 redeemPrice); function getRedeemPrice(address idleYieldToken, address user) external view returns (uint256 redeemPrice); } pragma solidity 0.5.16; import "@openzeppelin/contracts/math/Math.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../../Controllable.sol"; import "../../interfaces/IStrategy.sol"; import "../../uniswap/interfaces/IUniswapV2Router02.sol"; import "./IdleToken.sol"; import "./IIdleTokenHelper.sol"; import "../RewardTokenProfitNotifier.sol"; import "../../interfaces/IVault.sol"; contract IdleFinanceStrategy is IStrategy, RewardTokenProfitNotifier { using SafeMath for uint256; using SafeERC20 for IERC20; event ProfitsNotCollected(address); event Liquidating(address, uint256); IERC20 public underlying; address public idleUnderlying; uint256 public virtualPrice; IIdleTokenHelper public idleTokenHelper; address public vault; address public comp; address public idle; address[] public uniswapComp; address[] public uniswapIdle; address public uniswapRouterV2; bool public sellComp; bool public sellIdle; bool public claimAllowed; bool public protected; // These tokens cannot be claimed by the controller mapping(address => bool) public unsalvagableTokens; modifier restricted() { require( msg.sender == vault || msg.sender == address(controller()) || msg.sender == address(governance()), "The sender has to be the controller or vault or governance" ); _; } modifier updateVirtualPrice() { if (protected) { require( virtualPrice <= idleTokenHelper.getRedeemPrice(idleUnderlying), "virtual price is higher than needed" ); } _; virtualPrice = idleTokenHelper.getRedeemPrice(idleUnderlying); } constructor( address _storage, address _underlying, address _idleUnderlying, address _vault, address _comp, address _idle, address _weth, address _uniswap ) public RewardTokenProfitNotifier(_storage, _idle) { comp = _comp; idle = _idle; underlying = IERC20(_underlying); idleUnderlying = _idleUnderlying; vault = _vault; uniswapRouterV2 = _uniswap; protected = true; // set these tokens to be not salvagable unsalvagableTokens[_underlying] = true; unsalvagableTokens[_idleUnderlying] = true; unsalvagableTokens[_comp] = true; unsalvagableTokens[_idle] = true; uniswapComp = [_comp, _weth, _idle]; uniswapIdle = [_idle, _weth, _underlying]; idleTokenHelper = IIdleTokenHelper( 0x04Ce60ed10F6D2CfF3AA015fc7b950D13c113be5 ); virtualPrice = idleTokenHelper.getRedeemPrice(idleUnderlying); } function depositArbCheck() public view returns (bool) { return true; } /** * The strategy invests by supplying the underlying token into IDLE. */ function investAllUnderlying() public restricted updateVirtualPrice { uint256 balance = underlying.balanceOf(address(this)); underlying.safeApprove(address(idleUnderlying), 0); underlying.safeApprove(address(idleUnderlying), balance); IIdleTokenV3_1(idleUnderlying).mintIdleToken(balance, true, address(0)); } /** * Exits IDLE and transfers everything to the vault. */ function withdrawAllToVault() external restricted updateVirtualPrice { withdrawAll(); IERC20(address(underlying)).safeTransfer( vault, underlying.balanceOf(address(this)) ); } /** * Withdraws all from IDLE */ function withdrawAll() internal { uint256 balance = IERC20(idleUnderlying).balanceOf(address(this)); // this automatically claims the crops IIdleTokenV3_1(idleUnderlying).redeemIdleToken(balance); liquidateComp(); liquidateIdle(); } function withdrawToVault(uint256 amountUnderlying) public restricted { // this method is called when the vault is missing funds // we will calculate the proportion of idle LP tokens that matches // the underlying amount requested uint256 balanceBefore = underlying.balanceOf(address(this)); uint256 totalIdleLpTokens = IERC20(idleUnderlying).balanceOf(address(this)); uint256 totalUnderlyingBalance = totalIdleLpTokens.mul(virtualPrice).div(1e18); uint256 ratio = amountUnderlying.mul(1e18).div(totalUnderlyingBalance); uint256 toRedeem = totalIdleLpTokens.mul(ratio).div(1e18); IIdleTokenV3_1(idleUnderlying).redeemIdleToken(toRedeem); uint256 balanceAfter = underlying.balanceOf(address(this)); underlying.safeTransfer(vault, balanceAfter.sub(balanceBefore)); } /** * Withdraws all assets, liquidates COMP, and invests again in the required ratio. */ function forceUnleashed() public restricted updateVirtualPrice { if (claimAllowed) { claim(); } liquidateComp(); liquidateIdle(); // this updates the virtual price investAllUnderlying(); // state of supply/loan will be updated by the modifier } /** * Salvages a token. */ function salvage( address recipient, address token, uint256 amount ) public onlyGovernance { // To make sure that governance cannot come in and take away the coins require( !unsalvagableTokens[token], "token is defined as not salvagable" ); IERC20(token).safeTransfer(recipient, amount); } function claim() internal { IIdleTokenV3_1(idleUnderlying).redeemIdleToken(0); } function liquidateComp() internal { if (!sellComp) { // Profits can be disabled for possible simplified and rapid exit emit ProfitsNotCollected(comp); return; } // no profit notification, comp is liquidated to IDLE and will be notified there uint256 compBalance = IERC20(comp).balanceOf(address(this)); if (compBalance > 0) { emit Liquidating(address(comp), compBalance); IERC20(comp).safeApprove(uniswapRouterV2, 0); IERC20(comp).safeApprove(uniswapRouterV2, compBalance); // we can accept 1 as the minimum because this will be called only by a trusted worker IUniswapV2Router02(uniswapRouterV2).swapExactTokensForTokens( compBalance, 1, uniswapComp, address(this), block.timestamp ); } } function liquidateIdle() internal { if (!sellIdle) { // Profits can be disabled for possible simplified and rapid exit emit ProfitsNotCollected(idle); return; } uint256 rewardBalance = IERC20(idle).balanceOf(address(this)); notifyProfitInRewardToken(rewardBalance); uint256 idleBalance = IERC20(idle).balanceOf(address(this)); if (idleBalance > 0) { emit Liquidating(address(idle), idleBalance); IERC20(idle).safeApprove(uniswapRouterV2, 0); IERC20(idle).safeApprove(uniswapRouterV2, idleBalance); // we can accept 1 as the minimum because this will be called only by a trusted worker IUniswapV2Router02(uniswapRouterV2).swapExactTokensForTokens( idleBalance, 1, uniswapIdle, address(this), block.timestamp ); } } /** * Returns the current balance. Ignores COMP that was not liquidated and invested. */ function investedUnderlyingBalance() public view returns (uint256) { // NOTE: The use of virtual price is okay for appreciating assets inside IDLE, // but would be wrong and exploitable if funds were lost by IDLE, indicated by // the virtualPrice being greater than the token price. if (protected) { require( virtualPrice <= idleTokenHelper.getRedeemPrice(idleUnderlying), "virtual price is higher than needed" ); } uint256 invested = IERC20(idleUnderlying) .balanceOf(address(this)) .mul(virtualPrice) .div(1e18); return invested.add(IERC20(underlying).balanceOf(address(this))); } function setLiquidation( bool _sellComp, bool _sellIdle, bool _claimAllowed ) public onlyGovernance { sellComp = _sellComp; sellIdle = _sellIdle; claimAllowed = _claimAllowed; } function setProtected(bool _protected) public onlyGovernance { protected = _protected; } } pragma solidity 0.5.16; import "./IdleFinanceStrategy.sol"; /** * Adds the mainnet addresses to the PickleStrategy3Pool */ contract IdleStrategyUSDTMainnet is IdleFinanceStrategy { // token addresses address public constant __weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); address public constant __usdt = address(0xdAC17F958D2ee523a2206206994597C13D831ec7); address public constant __uniswap = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); address public constant __idleUnderlying = address(0xF34842d05A1c888Ca02769A633DF37177415C2f8); address public constant __comp = address(0xc00e94Cb662C3520282E6f5717214004A7f26888); address public constant __idle = address(0x875773784Af8135eA0ef43b5a374AaD105c5D39e); constructor(address _storage, address _vault) public IdleFinanceStrategy( _storage, __usdt, __idleUnderlying, _vault, __comp, __idle, __weth, __uniswap ) {} } /** * @title: Idle Token interface * @author: Idle Labs Inc., idle.finance */ pragma solidity 0.5.16; interface IIdleTokenV3_1 { // view /** * IdleToken price calculation, in underlying * * @return : price in underlying token */ function tokenPrice() external view returns (uint256 price); /** * @return : underlying token address */ function token() external view returns (address); /** * Get APR of every ILendingProtocol * * @return addresses: array of token addresses * @return aprs: array of aprs (ordered in respect to the `addresses` array) */ function getAPRs() external view returns (address[] memory addresses, uint256[] memory aprs); // external // We should save the amount one has deposited to calc interests /** * Used to mint IdleTokens, given an underlying amount (eg. DAI). * This method triggers a rebalance of the pools if needed * NOTE: User should 'approve' _amount of tokens before calling mintIdleToken * NOTE 2: this method can be paused * * @param _amount : amount of underlying token to be lended * @param _skipRebalance : flag for skipping rebalance for lower gas price * @param _referral : referral address * @return mintedTokens : amount of IdleTokens minted */ function mintIdleToken( uint256 _amount, bool _skipRebalance, address _referral ) external returns (uint256 mintedTokens); /** * Here we calc the pool share one can withdraw given the amount of IdleToken they want to burn * This method triggers a rebalance of the pools if needed * NOTE: If the contract is paused or iToken price has decreased one can still redeem but no rebalance happens. * NOTE 2: If iToken price has decresed one should not redeem (but can do it) otherwise he would capitalize the loss. * Ideally one should wait until the black swan event is terminated * * @param _amount : amount of IdleTokens to be burned * @return redeemedTokens : amount of underlying tokens redeemed */ function redeemIdleToken(uint256 _amount) external returns (uint256 redeemedTokens); /** * Here we calc the pool share one can withdraw given the amount of IdleToken they want to burn * and send interest-bearing tokens (eg. cDAI/iDAI) directly to the user. * Underlying (eg. DAI) is not redeemed here. * * @param _amount : amount of IdleTokens to be burned */ function redeemInterestBearingTokens(uint256 _amount) external; /** * @return : whether has rebalanced or not */ function rebalance() external returns (bool); } pragma solidity >=0.5.0; interface IUniswapV2Router01 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns ( uint256 amountA, uint256 amountB, uint256 liquidity ); function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable returns ( uint256 amountToken, uint256 amountETH, uint256 liquidity ); function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETH( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountToken, uint256 amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETHWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountToken, uint256 amountETH); function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactETHForTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function swapTokensForExactETH( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapETHForExactTokens( uint256 amountOut, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) external pure returns (uint256 amountB); function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountOut); function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountIn); function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); } pragma solidity >=0.5.0; import "./IUniswapV2Router01.sol"; interface IUniswapV2Router02 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns ( uint256 amountA, uint256 amountB, uint256 liquidity ); function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable returns ( uint256 amountToken, uint256 amountETH, uint256 liquidity ); function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETH( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountToken, uint256 amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETHWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountToken, uint256 amountETH); function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactETHForTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function swapTokensForExactETH( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapETHForExactTokens( uint256 amountOut, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) external pure returns (uint256 amountB); function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountOut); function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountIn); function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountETH); function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountETH); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; } pragma solidity ^0.5.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } } pragma solidity ^0.5.0; /** * @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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } pragma solidity ^0.5.0; import "./IERC20.sol"; /** * @dev Optional functions from the ERC20 standard. */ contract ERC20Detailed is IERC20 { string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of * these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol, uint8 decimals) public { _name = name; _symbol = symbol; _decimals = decimals; } /** * @dev Returns the name of the token. */ function name() public 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 _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. * * 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 _decimals; } } pragma solidity ^0.5.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see {ERC20Detailed}. */ 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); } pragma solidity ^0.5.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/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 ERC20;` 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 { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length 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 safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length 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"); } } } pragma solidity ^0.5.5; /** * @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 Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * _Available since v2.4.0._ */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } /** * @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]. * * _Available since v2.4.0._ */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-call-value (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } }
File 5 of 11: IdleTokenHelper
// File: @openzeppelin/contracts/math/SafeMath.sol pragma solidity ^0.6.0; /** * @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) { 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; } } // File: interfaces/Idle/IIdleTokenV3_1.sol pragma solidity 0.6.12; interface IIdleTokenV3_1 { // view /** * IdleToken price calculation not considering fees, in underlying * * @return price : price in underlying token */ function tokenPrice() external view returns (uint256 price); // view /** * Map which saves avg idleToken minting price per user * Used in calculating redeem price * * @return price : price in underlying token */ function userAvgPrices(address user) external view returns (uint256 price); // view /** * Current fee on interest gained * * @return fee : fee on interest gained */ function fee() external view returns (uint256 fee); /** * @return underlying : underlying token address */ function token() external view returns (address underlying); /** * Get APR of every ILendingProtocol * * @return addresses : array of token addresses * @return aprs : array of aprs (ordered in respect to the `addresses` array) */ function getAPRs() external view returns (address[] memory addresses, uint256[] memory aprs); // external // We should save the amount one has deposited to calc interests /** * Used to mint IdleTokens, given an underlying amount (eg. DAI). * This method triggers a rebalance of the pools if needed * NOTE: User should 'approve' _amount of tokens before calling mintIdleToken * NOTE 2: this method can be paused * * @param _amount : amount of underlying token to be lended * @param _skipRebalance : flag for skipping rebalance for lower gas price * @param _referral : referral address * @return mintedTokens : amount of IdleTokens minted */ function mintIdleToken(uint256 _amount, bool _skipRebalance, address _referral) external returns (uint256 mintedTokens); /** * Here we calc the pool share one can withdraw given the amount of IdleToken they want to burn * This method triggers a rebalance of the pools if needed * NOTE: If the contract is paused or iToken price has decreased one can still redeem but no rebalance happens. * NOTE 2: If iToken price has decresed one should not redeem (but can do it) otherwise he would capitalize the loss. * Ideally one should wait until the black swan event is terminated * * @param _amount : amount of IdleTokens to be burned * @return redeemedTokens : amount of underlying tokens redeemed */ function redeemIdleToken(uint256 _amount) external returns (uint256 redeemedTokens); /** * Here we calc the pool share one can withdraw given the amount of IdleToken they want to burn * and send interest-bearing tokens (eg. cDAI/iDAI) directly to the user. * Underlying (eg. DAI) is not redeemed here. * * @param _amount : amount of IdleTokens to be burned */ function redeemInterestBearingTokens(uint256 _amount) external; /** * @return : whether has rebalanced or not */ function rebalance() external returns (bool); } // File: contracts/IdleTokenHelper.sol // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.6.12; contract IdleTokenHelper { using SafeMath for uint256; uint256 constant public FULL_ALLOC = 100000; function getMintingPrice(address idleYieldToken) view external returns (uint256 mintingPrice) { return _getMintingPrice(idleYieldToken); } function _getMintingPrice(address idleYieldToken) view internal returns (uint256 mintingPrice) { return IIdleTokenV3_1(idleYieldToken).tokenPrice(); } function getRedeemPrice(address idleYieldToken) view external returns (uint256 redeemPrice) { return _getRedeemPrice(idleYieldToken, msg.sender); } function getRedeemPrice(address idleYieldToken, address user) view external returns (uint256 redeemPrice) { return _getRedeemPrice(idleYieldToken, user); } function _getRedeemPrice(address idleYieldToken, address user) view internal returns (uint256 redeemPrice) { /* * As per https://github.com/Idle-Labs/idle-contracts/blob/ad0f18fef670ea6a4030fe600f64ece3d3ac2202/contracts/IdleTokenGovernance.sol#L878-L900 * * Price on minting is currentPrice * Price on redeem must consider the fee * * Below the implementation of the following redeemPrice formula * * redeemPrice := underlyingAmount/idleTokenAmount * * redeemPrice = currentPrice * (1 - scaledFee * ΔP%) * * where: * - scaledFee := fee/FULL_ALLOC * - ΔP% := 0 when currentPrice < userAvgPrice (no gain) and (currentPrice-userAvgPrice)/currentPrice * * n.b: gain := idleTokenAmount * ΔP% * currentPrice */ IIdleTokenV3_1 iyt = IIdleTokenV3_1(idleYieldToken); uint256 userAvgPrice = iyt.userAvgPrices(user); uint256 currentPrice = iyt.tokenPrice(); // When no deposits userAvgPrice is 0 equiv currentPrice // and in the case of issues if (userAvgPrice == 0 || currentPrice < userAvgPrice) { redeemPrice = currentPrice; } else { uint256 fee = iyt.fee(); redeemPrice = ((currentPrice.mul(FULL_ALLOC)) .sub( fee.mul( currentPrice.sub(userAvgPrice) ) )).div(FULL_ALLOC); } return redeemPrice; } }
File 6 of 11: AdminUpgradeabilityProxy
// File: @openzeppelin/upgrades/contracts/upgradeability/Proxy.sol pragma solidity ^0.5.0; /** * @title Proxy * @dev Implements delegation of calls to other contracts, with proper * forwarding of return values and bubbling of failures. * It defines a fallback function that delegates all calls to the address * returned by the abstract _implementation() internal function. */ contract Proxy { /** * @dev Fallback function. * Implemented entirely in `_fallback`. */ function () payable external { _fallback(); } /** * @return The Address of the implementation. */ function _implementation() internal view returns (address); /** * @dev Delegates execution to an implementation contract. * This is a low level function that doesn't return to its internal call site. * It will return to the external caller whatever the implementation returns. * @param implementation Address to delegate. */ function _delegate(address implementation) internal { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize) } default { return(0, returndatasize) } } } /** * @dev Function that is run as the first thing in the fallback function. * Can be redefined in derived contracts to add functionality. * Redefinitions must call super._willFallback(). */ function _willFallback() internal { } /** * @dev fallback implementation. * Extracted to enable manual triggering. */ function _fallback() internal { _willFallback(); _delegate(_implementation()); } } // File: @openzeppelin/upgrades/contracts/utils/Address.sol pragma solidity ^0.5.0; /** * Utility library of inline functions on addresses * * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version. */ library OpenZeppelinUpgradesAddress { /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract, * as the code is not actually created until after the constructor finishes. * @param account address of the account to check * @return whether the target address is a contract */ function isContract(address account) internal view returns (bool) { uint256 size; // XXX Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address. // See https://ethereum.stackexchange.com/a/14016/36603 // for more details about how this works. // TODO Check this again before the Serenity release, because all addresses will be // contracts then. // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } // File: @openzeppelin/upgrades/contracts/upgradeability/BaseUpgradeabilityProxy.sol pragma solidity ^0.5.0; /** * @title BaseUpgradeabilityProxy * @dev This contract implements a proxy that allows to change the * implementation address to which it will delegate. * Such a change is called an implementation upgrade. */ contract BaseUpgradeabilityProxy is Proxy { /** * @dev Emitted when the implementation is upgraded. * @param implementation Address of the new implementation. */ event Upgraded(address indexed implementation); /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Returns the current implementation. * @return Address of the current implementation */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Upgrades the proxy to a new implementation. * @param newImplementation Address of the new implementation. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Sets the implementation address of the proxy. * @param newImplementation Address of the new implementation. */ function _setImplementation(address newImplementation) internal { require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, newImplementation) } } } // File: @openzeppelin/upgrades/contracts/upgradeability/UpgradeabilityProxy.sol pragma solidity ^0.5.0; /** * @title UpgradeabilityProxy * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing * implementation and init data. */ contract UpgradeabilityProxy is BaseUpgradeabilityProxy { /** * @dev Contract constructor. * @param _logic Address of the initial implementation. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ constructor(address _logic, bytes memory _data) public payable { assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); _setImplementation(_logic); if(_data.length > 0) { (bool success,) = _logic.delegatecall(_data); require(success); } } } // File: @openzeppelin/upgrades/contracts/upgradeability/BaseAdminUpgradeabilityProxy.sol pragma solidity ^0.5.0; /** * @title BaseAdminUpgradeabilityProxy * @dev This contract combines an upgradeability proxy with an authorization * mechanism for administrative tasks. * All external functions in this contract must be guarded by the * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity * feature proposal that would enable this to be done automatically. */ contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy { /** * @dev Emitted when the administration has been transferred. * @param previousAdmin Address of the previous admin. * @param newAdmin Address of the new admin. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Modifier to check whether the `msg.sender` is the admin. * If it is, it will run the function. Otherwise, it will delegate the call * to the implementation. */ modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } /** * @return The address of the proxy admin. */ function admin() external ifAdmin returns (address) { return _admin(); } /** * @return The address of the implementation. */ function implementation() external ifAdmin returns (address) { return _implementation(); } /** * @dev Changes the admin of the proxy. * Only the current admin can call this function. * @param newAdmin Address to transfer proxy administration to. */ function changeAdmin(address newAdmin) external ifAdmin { require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); emit AdminChanged(_admin(), newAdmin); _setAdmin(newAdmin); } /** * @dev Upgrade the backing implementation of the proxy. * Only the admin can call this function. * @param newImplementation Address of the new implementation. */ function upgradeTo(address newImplementation) external ifAdmin { _upgradeTo(newImplementation); } /** * @dev Upgrade the backing implementation of the proxy and call a function * on the new implementation. * This is useful to initialize the proxied contract. * @param newImplementation Address of the new implementation. * @param data Data to send as msg.data in the low level call. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. */ function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin { _upgradeTo(newImplementation); (bool success,) = newImplementation.delegatecall(data); require(success); } /** * @return The admin slot. */ function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) } } /** * @dev Sets the address of the proxy admin. * @param newAdmin Address of the new proxy admin. */ function _setAdmin(address newAdmin) internal { bytes32 slot = ADMIN_SLOT; assembly { sstore(slot, newAdmin) } } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal { require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); super._willFallback(); } } // File: @openzeppelin/upgrades/contracts/upgradeability/AdminUpgradeabilityProxy.sol pragma solidity ^0.5.0; /** * @title AdminUpgradeabilityProxy * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for * initializing the implementation, admin, and init data. */ contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy { /** * Contract constructor. * @param _logic address of the initial implementation. * @param _admin Address of the proxy administrator. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable { assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); _setAdmin(_admin); } }
File 7 of 11: IdleTokenGovernance
// File: @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol pragma solidity ^0.5.0; /** * @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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol pragma solidity ^0.5.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see {ERC20Detailed}. */ 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); } // File: @openzeppelin/contracts-ethereum-package/contracts/utils/Address.sol pragma solidity ^0.5.5; /** * @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 Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * _Available since v2.4.0._ */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } /** * @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]. * * _Available since v2.4.0._ */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-call-value (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/SafeERC20.sol pragma solidity ^0.5.0; /** * @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 ERC20;` 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 { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length 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 safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length 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"); } } } // File: @openzeppelin/upgrades/contracts/Initializable.sol pragma solidity >=0.4.24 <0.7.0; /** * @title Initializable * * @dev Helper contract to support initializer functions. To use it, replace * the constructor with a function that has the `initializer` modifier. * WARNING: Unlike constructors, initializer functions must be manually * invoked. This applies both to deploying an Initializable contract, as well * as extending an Initializable contract via inheritance. * WARNING: When used with inheritance, manual care must be taken to not invoke * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. */ contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private initializing; /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; initialized = true; } _; if (isTopLevelCall) { initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function isConstructor() private view returns (bool) { // extcodesize checks the size of the code stored in an address, and // address returns the current address. Since the code is still not // deployed when running a constructor, any checks on its code size will // yield zero, making it an effective way to detect if a contract is // under construction or not. address self = address(this); uint256 cs; assembly { cs := extcodesize(self) } return cs == 0; } // Reserved storage space to allow for layout changes in the future. uint256[50] private ______gap; } // File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol pragma solidity ^0.5.0; /* * @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. */ contract Context is Initializable { // Empty internal constructor, to prevent people from mistakenly deploying // an instance of this contract, which should be used via inheritance. constructor () internal { } // solhint-disable-previous-line no-empty-blocks function _msgSender() internal view returns (address payable) { return msg.sender; } function _msgData() internal view returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol pragma solidity ^0.5.0; /** * @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 {ERC20Mintable}. * * 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 Initializable, Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public 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`. */ function transfer(address recipient, uint256 amount) public returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public 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`. */ function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @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. */ function increaseAllowance(address spender, uint256 addedValue) public 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`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); 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, uint256 amount) internal { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); 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 { require(account != address(0), "ERC20: mint to the zero address"); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); 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 { require(account != address(0), "ERC20: burn from the zero address"); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); 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, uint256 amount) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Destroys `amount` tokens from `account`.`amount` is then deducted * from the caller's allowance. * * See {_burn} and {_approve}. */ function _burnFrom(address account, uint256 amount) internal { _burn(account, amount); _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); } uint256[50] private ______gap; } // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol pragma solidity ^0.5.0; /** * @dev Optional functions from the ERC20 standard. */ contract ERC20Detailed is Initializable, IERC20 { string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of * these values are immutable: they can only be set once during * construction. */ function initialize(string memory name, string memory symbol, uint8 decimals) public initializer { _name = name; _symbol = symbol; _decimals = decimals; } /** * @dev Returns the name of the token. */ function name() public 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 _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. * * 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 _decimals; } uint256[50] private ______gap; } // File: @openzeppelin/contracts-ethereum-package/contracts/utils/ReentrancyGuard.sol pragma solidity ^0.5.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. */ contract ReentrancyGuard is Initializable { // counter to allow mutex lock with only one SSTORE operation uint256 private _guardCounter; function initialize() public initializer { // The counter starts at one to prevent changing it from zero to a non-zero // value, which is a more expensive operation. _guardCounter = 1; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { _guardCounter += 1; uint256 localCounter = _guardCounter; _; require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call"); } uint256[50] private ______gap; } // File: @openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol pragma solidity ^0.5.0; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be aplied to your functions to restrict their use to * the owner. */ contract Ownable is Initializable, Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function initialize(address sender) public initializer { _owner = sender; emit OwnershipTransferred(address(0), _owner); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } /** * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { return _msgSender() == _owner; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * > Note: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } uint256[50] private ______gap; } // File: @openzeppelin/contracts-ethereum-package/contracts/access/Roles.sol pragma solidity ^0.5.0; /** * @title Roles * @dev Library for managing addresses assigned to a Role. */ library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev Give an account access to this role. */ function add(Role storage role, address account) internal { require(!has(role, account), "Roles: account already has role"); role.bearer[account] = true; } /** * @dev Remove an account's access to this role. */ function remove(Role storage role, address account) internal { require(has(role, account), "Roles: account does not have role"); role.bearer[account] = false; } /** * @dev Check if an account has this role. * @return bool */ function has(Role storage role, address account) internal view returns (bool) { require(account != address(0), "Roles: account is the zero address"); return role.bearer[account]; } } // File: @openzeppelin/contracts-ethereum-package/contracts/access/roles/PauserRole.sol pragma solidity ^0.5.0; contract PauserRole is Initializable, Context { using Roles for Roles.Role; event PauserAdded(address indexed account); event PauserRemoved(address indexed account); Roles.Role private _pausers; function initialize(address sender) public initializer { if (!isPauser(sender)) { _addPauser(sender); } } modifier onlyPauser() { require(isPauser(_msgSender()), "PauserRole: caller does not have the Pauser role"); _; } function isPauser(address account) public view returns (bool) { return _pausers.has(account); } function addPauser(address account) public onlyPauser { _addPauser(account); } function renouncePauser() public { _removePauser(_msgSender()); } function _addPauser(address account) internal { _pausers.add(account); emit PauserAdded(account); } function _removePauser(address account) internal { _pausers.remove(account); emit PauserRemoved(account); } uint256[50] private ______gap; } // File: @openzeppelin/contracts-ethereum-package/contracts/lifecycle/Pausable.sol pragma solidity ^0.5.0; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ contract Pausable is Initializable, Context, PauserRole { /** * @dev Emitted when the pause is triggered by a pauser (`account`). */ event Paused(address account); /** * @dev Emitted when the pause is lifted by a pauser (`account`). */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. Assigns the Pauser role * to the deployer. */ function initialize(address sender) public initializer { PauserRole.initialize(sender); _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!_paused, "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(_paused, "Pausable: not paused"); _; } /** * @dev Called by a pauser to pause, triggers stopped state. */ function pause() public onlyPauser whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Called by a pauser to unpause, returns to normal state. */ function unpause() public onlyPauser whenPaused { _paused = false; emit Unpaused(_msgSender()); } uint256[50] private ______gap; } // File: contracts/interfaces/iERC20Fulcrum.sol pragma solidity 0.5.16; interface iERC20Fulcrum { function mint( address receiver, uint256 depositAmount) external returns (uint256 mintAmount); function burn( address receiver, uint256 burnAmount) external returns (uint256 loanAmountPaid); function tokenPrice() external view returns (uint256 price); function supplyInterestRate() external view returns (uint256); function rateMultiplier() external view returns (uint256); function baseRate() external view returns (uint256); function borrowInterestRate() external view returns (uint256); function avgBorrowInterestRate() external view returns (uint256); function protocolInterestRate() external view returns (uint256); function spreadMultiplier() external view returns (uint256); function totalAssetBorrow() external view returns (uint256); function totalAssetSupply() external view returns (uint256); function nextSupplyInterestRate(uint256) external view returns (uint256); function nextBorrowInterestRate(uint256) external view returns (uint256); function nextLoanInterestRate(uint256) external view returns (uint256); function totalSupplyInterestRate(uint256) external view returns (uint256); function claimLoanToken() external returns (uint256 claimedAmount); function dsr() external view returns (uint256); function chaiPrice() external view returns (uint256); } // File: contracts/interfaces/ILendingProtocol.sol pragma solidity 0.5.16; interface ILendingProtocol { function mint() external returns (uint256); function redeem(address account) external returns (uint256); function nextSupplyRate(uint256 amount) external view returns (uint256); function nextSupplyRateWithParams(uint256[] calldata params) external view returns (uint256); function getAPR() external view returns (uint256); function getPriceInToken() external view returns (uint256); function token() external view returns (address); function underlying() external view returns (address); function availableLiquidity() external view returns (uint256); } // File: contracts/interfaces/IGovToken.sol pragma solidity 0.5.16; interface IGovToken { function redeemGovTokens() external; } // File: contracts/interfaces/IIdleTokenV3_1.sol /** * @title: Idle Token interface * @author: Idle Labs Inc., idle.finance */ pragma solidity 0.5.16; interface IIdleTokenV3_1 { // view /** * IdleToken price calculation, in underlying * * @return : price in underlying token */ function tokenPrice() external view returns (uint256 price); /** * @return : underlying token address */ function token() external view returns (address); /** * Get APR of every ILendingProtocol * * @return addresses: array of token addresses * @return aprs: array of aprs (ordered in respect to the `addresses` array) */ function getAPRs() external view returns (address[] memory addresses, uint256[] memory aprs); // external // We should save the amount one has deposited to calc interests /** * Used to mint IdleTokens, given an underlying amount (eg. DAI). * This method triggers a rebalance of the pools if needed * NOTE: User should 'approve' _amount of tokens before calling mintIdleToken * NOTE 2: this method can be paused * * @param _amount : amount of underlying token to be lended * @param _skipRebalance : flag for skipping rebalance for lower gas price * @param _referral : referral address * @return mintedTokens : amount of IdleTokens minted */ function mintIdleToken(uint256 _amount, bool _skipRebalance, address _referral) external returns (uint256 mintedTokens); /** * Here we calc the pool share one can withdraw given the amount of IdleToken they want to burn * This method triggers a rebalance of the pools if needed * NOTE: If the contract is paused or iToken price has decreased one can still redeem but no rebalance happens. * NOTE 2: If iToken price has decresed one should not redeem (but can do it) otherwise he would capitalize the loss. * Ideally one should wait until the black swan event is terminated * * @param _amount : amount of IdleTokens to be burned * @return redeemedTokens : amount of underlying tokens redeemed */ function redeemIdleToken(uint256 _amount) external returns (uint256 redeemedTokens); /** * Here we calc the pool share one can withdraw given the amount of IdleToken they want to burn * and send interest-bearing tokens (eg. cDAI/iDAI) directly to the user. * Underlying (eg. DAI) is not redeemed here. * * @param _amount : amount of IdleTokens to be burned */ function redeemInterestBearingTokens(uint256 _amount) external; /** * @return : whether has rebalanced or not */ function rebalance() external returns (bool); } // File: contracts/interfaces/Comptroller.sol pragma solidity 0.5.16; interface Comptroller { function claimComp(address) external; function compSpeeds(address _cToken) external view returns (uint256); function claimComp(address[] calldata holders, address[] calldata cTokens, bool borrowers, bool suppliers) external; } // File: contracts/interfaces/CERC20.sol pragma solidity 0.5.16; interface CERC20 { function mint(uint256 mintAmount) external returns (uint256); function comptroller() external view returns (address); function redeem(uint256 redeemTokens) external returns (uint256); function exchangeRateStored() external view returns (uint256); function supplyRatePerBlock() external view returns (uint256); function borrowRatePerBlock() external view returns (uint256); function totalReserves() external view returns (uint256); function getCash() external view returns (uint256); function totalBorrows() external view returns (uint256); function reserveFactorMantissa() external view returns (uint256); function interestRateModel() external view returns (address); } // File: contracts/interfaces/IdleController.sol pragma solidity 0.5.16; interface IdleController { function idleSpeeds(address _idleToken) external view returns (uint256); function claimIdle(address[] calldata holders, address[] calldata idleTokens) external; } // File: contracts/interfaces/PriceOracle.sol pragma solidity 0.5.16; interface PriceOracle { function getUnderlyingPrice(address _idleToken) external view returns (uint256); function getPriceUSD(address _asset) external view returns (uint256 price); function getPriceETH(address _asset) external view returns (uint256 price); function getPriceToken(address _asset, address _token) external view returns (uint256 price); function WETH() external view returns (address); function getCompApr(address cToken, address token) external view returns (uint256); } // File: contracts/interfaces/GasToken.sol pragma solidity 0.5.16; interface GasToken { function freeUpTo(uint256 value) external returns (uint256 freed); function freeFromUpTo(address from, uint256 value) external returns (uint256 freed); function balanceOf(address from) external returns (uint256 balance); } // File: contracts/GST2ConsumerV2.sol pragma solidity 0.5.16; contract GST2ConsumerV2 is Initializable { GasToken public gst2; function initialize() initializer public { gst2 = GasToken(0x0000000000b3F879cb30FE243b4Dfee438691c04); } modifier gasDiscountFrom(address from) { uint256 initialGasLeft = gasleft(); _; _makeGasDiscount(initialGasLeft - gasleft(), from); } function _makeGasDiscount(uint256 gasSpent, address from) internal { // For more info https://gastoken.io/ // 14154 -> FREE_BASE -> base cost of freeing // 41130 -> 2 * REIMBURSE - FREE_TOKEN -> 2 * 24000 - 6870 uint256 tokens = (gasSpent + 14154) / 41130; uint256 safeNumTokens; uint256 gas = gasleft(); // For more info https://github.com/projectchicago/gastoken/blob/master/contract/gst2_free_example.sol if (gas >= 27710) { safeNumTokens = (gas - 27710) / 7020; } if (tokens > safeNumTokens) { tokens = safeNumTokens; } if (tokens > 0) { gst2.freeFromUpTo(from, tokens); } } } // File: contracts/IdleTokenGovernance.sol /** * @title: Idle Token (V3) main contract * @summary: ERC20 that holds pooled user funds together * Each token rapresent a share of the underlying pools * and with each token user have the right to redeem a portion of these pools * @author: Idle Labs Inc., idle.finance */ pragma solidity 0.5.16; contract IdleTokenGovernance is Initializable, ERC20, ERC20Detailed, ReentrancyGuard, Ownable, Pausable, IIdleTokenV3_1, GST2ConsumerV2 { using SafeERC20 for IERC20; using SafeMath for uint256; uint256 private constant ONE_18 = 10**18; // State variables // eg. DAI address address public token; // eg. iDAI address address private iToken; // eg. cDAI address address private cToken; // Idle rebalancer current implementation address address public rebalancer; // Address collecting underlying fees address public feeAddress; // Last iToken price, used to pause contract in case of a black swan event uint256 public lastITokenPrice; // eg. 18 for DAI uint256 private tokenDecimals; // Max unlent assets percentage for gas friendly swaps uint256 public maxUnlentPerc; // 100000 == 100% -> 1000 == 1% // Current fee on interest gained uint256 public fee; // eg. [cTokenAddress, iTokenAddress, ...] address[] public allAvailableTokens; // eg. [COMPAddress, CRVAddress, ...] address[] public govTokens; // last fully applied allocations (ie when all liquidity has been correctly placed) // eg. [5000, 0, 5000, 0] for 50% in compound, 0% fulcrum, 50% aave, 0 dydx. same order of allAvailableTokens uint256[] public lastAllocations; // Map that saves avg idleToken price paid for each user, used to calculate earnings mapping(address => uint256) public userAvgPrices; // eg. cTokenAddress => IdleCompoundAddress mapping(address => address) public protocolWrappers; // array with last balance recorded for each gov tokens mapping (address => uint256) public govTokensLastBalances; // govToken -> user_address -> user_index eg. usersGovTokensIndexes[govTokens[0]][msg.sender] = 1111123; mapping (address => mapping (address => uint256)) public usersGovTokensIndexes; // global indices for each gov tokens used as a reference to calculate a fair share for each user mapping (address => uint256) public govTokensIndexes; // Map that saves amount with no fee for each user mapping(address => uint256) private userNoFeeQty; // variable used for avoid the call of mint and redeem in the same tx bytes32 private _minterBlock; // Events event Rebalance(address _rebalancer, uint256 _amount); event Referral(uint256 _amount, address _ref); // ########## IdleToken V4_1 updates // Idle governance token address public constant IDLE = address(0x875773784Af8135eA0ef43b5a374AaD105c5D39e); // Compound governance token address public constant COMP = address(0xc00e94Cb662C3520282E6f5717214004A7f26888); uint256 private constant FULL_ALLOC = 100000; // Idle distribution controller address public constant idleController = address(0x275DA8e61ea8E02d51EDd8d0DC5c0E62b4CDB0BE); // oracle used for calculating the avgAPR with gov tokens address public oracle; // eg cDAI -> COMP mapping(address => address) private protocolTokenToGov; // Whether openRebalance is enabled or not bool public isRiskAdjusted; // last allocations submitted by rebalancer uint256[] private lastRebalancerAllocations; // onlyOwner /** * It allows owner to modify allAvailableTokens array in case of emergency * ie if a bug on a interest bearing token is discovered and reset protocolWrappers * associated with those tokens. * * @param protocolTokens : array of protocolTokens addresses (eg [cDAI, iDAI, ...]) * @param wrappers : array of wrapper addresses (eg [IdleCompound, IdleFulcrum, ...]) * @param allocations : array of allocations * @param keepAllocations : whether to update lastRebalancerAllocations or not */ function setAllAvailableTokensAndWrappers( address[] calldata protocolTokens, address[] calldata wrappers, uint256[] calldata allocations, bool keepAllocations ) external onlyOwner { require(protocolTokens.length == wrappers.length && (allocations.length == wrappers.length || keepAllocations), "IDLE:LEN_DIFF"); for (uint256 i = 0; i < protocolTokens.length; i++) { require(protocolTokens[i] != address(0) && wrappers[i] != address(0), "IDLE:IS_0"); protocolWrappers[protocolTokens[i]] = wrappers[i]; } allAvailableTokens = protocolTokens; if (keepAllocations) { require(protocolTokens.length == allAvailableTokens.length, "IDLE:LEN_DIFF2"); return; } _setAllocations(allocations); } /** * It allows owner to set gov tokens array * In case of any errors gov distribution can be paused by passing an empty array * * @param _newGovTokens : array of governance token addresses * @param _protocolTokens : array of interest bearing token addresses */ function setGovTokens( address[] calldata _newGovTokens, address[] calldata _protocolTokens ) external onlyOwner { govTokens = _newGovTokens; // Reset protocolTokenToGov mapping for (uint256 i = 0; i < allAvailableTokens.length; i++) { protocolTokenToGov[allAvailableTokens[i]] = address(0); } // set protocol token to gov token mapping for (uint256 i = 0; i < _protocolTokens.length; i++) { address newGov = _newGovTokens[i]; if (newGov == IDLE) { continue; } protocolTokenToGov[_protocolTokens[i]] = _newGovTokens[i]; } } /** * It allows owner to set the IdleRebalancerV3_1 address * * @param _rebalancer : new IdleRebalancerV3_1 address */ function setRebalancer(address _rebalancer) external onlyOwner { require(_rebalancer != address(0), "IDLE:IS_0"); rebalancer = _rebalancer; } /** * It allows owner to set the fee (1000 == 10% of gained interest) * * @param _fee : fee amount where 100000 is 100%, max settable is 10% */ function setFee(uint256 _fee) external onlyOwner { // 100000 == 100% -> 10000 == 10% require(_fee <= 10000, "IDLE:TOO_HIGH"); fee = _fee; } /** * It allows owner to set the fee address * * @param _feeAddress : fee address */ function setFeeAddress(address _feeAddress) external onlyOwner { require(_feeAddress != address(0), "IDLE:IS_0"); feeAddress = _feeAddress; } /** * It allows owner to set the oracle address for getting avgAPR * * @param _oracle : new oracle address */ function setOracleAddress(address _oracle) external onlyOwner { require(_oracle != address(0), "IDLE:IS_0"); oracle = _oracle; } /** * It allows owner to set the max unlent asset percentage (1000 == 1% of unlent asset max) * * @param _perc : max unlent perc where 100000 is 100% */ function setMaxUnlentPerc(uint256 _perc) external onlyOwner { require(_perc <= 100000, "IDLE:TOO_HIGH"); maxUnlentPerc = _perc; } /** * It allows owner to set the isRiskAdjusted flag * * @param _isRiskAdjusted : flag for openRebalance */ function setIsRiskAdjusted(bool _isRiskAdjusted) external onlyOwner { isRiskAdjusted = _isRiskAdjusted; } /** * Used by Rebalancer to set the new allocations * * @param _allocations : array with allocations in percentages (100% => 100000) */ function setAllocations(uint256[] calldata _allocations) external { require(msg.sender == rebalancer || msg.sender == owner(), "IDLE:!AUTH"); _setAllocations(_allocations); } /** * Used by Rebalancer or in openRebalance to set the new allocations * * @param _allocations : array with allocations in percentages (100% => 100000) */ function _setAllocations(uint256[] memory _allocations) internal { require(_allocations.length == allAvailableTokens.length, "IDLE:!EQ_LEN"); uint256 total; for (uint256 i = 0; i < _allocations.length; i++) { total = total.add(_allocations[i]); } lastRebalancerAllocations = _allocations; require(total == FULL_ALLOC, "IDLE:!EQ_TOT"); } // view /** * Get latest allocations submitted by rebalancer * * @return : array of allocations ordered as allAvailableTokens */ function getAllocations() external view returns (uint256[] memory) { return lastRebalancerAllocations; } /** * IdleToken price calculation, in underlying * * @return : price in underlying token */ function tokenPrice() external view returns (uint256) { return _tokenPrice(); } /** * Get APR of every ILendingProtocol * * @return addresses: array of token addresses * @return aprs: array of aprs (ordered in respect to the `addresses` array) */ function getAPRs() external view returns (address[] memory addresses, uint256[] memory aprs) { address currToken; addresses = new address[](allAvailableTokens.length); aprs = new uint256[](allAvailableTokens.length); for (uint256 i = 0; i < allAvailableTokens.length; i++) { currToken = allAvailableTokens[i]; addresses[i] = currToken; aprs[i] = ILendingProtocol(protocolWrappers[currToken]).getAPR(); } } /** * Get current avg APR of this IdleToken * * @return avgApr: current weighted avg apr */ function getAvgAPR() external view returns (uint256) { return _getAvgAPR(); } /** * Get current avg APR of this IdleToken * * @return avgApr: current weighted avg apr */ function _getAvgAPR() internal view returns (uint256 avgApr) { (, uint256[] memory amounts, uint256 total) = _getCurrentAllocations(); // IDLE gov token won't be counted here because is not in allAvailableTokens for (uint256 i = 0; i < allAvailableTokens.length; i++) { if (amounts[i] == 0) { continue; } address protocolToken = allAvailableTokens[i]; // avgApr = avgApr.add(currApr.mul(weight).div(ONE_18)) avgApr = avgApr.add( ILendingProtocol(protocolWrappers[protocolToken]).getAPR().mul( amounts[i] ) ); // Add weighted gov tokens apr address currGov = protocolTokenToGov[protocolToken]; if (govTokens.length > 0 && currGov != address(0)) { avgApr = avgApr.add(amounts[i].mul(getGovApr(currGov))); } } avgApr = avgApr.div(total); } /** * Get gov token APR * * @return : apr scaled to 1e18 */ function getGovApr(address _govToken) internal view returns (uint256) { // In case new Gov tokens will be supported this should be updated, no need to add IDLE apr if (_govToken == COMP && cToken != address(0)) { return PriceOracle(oracle).getCompApr(cToken, token); } } /** * ERC20 modified transferFrom that also update the avgPrice paid for the recipient and * updates user gov idx * * @param sender : sender account * @param recipient : recipient account * @param amount : value to transfer * @return : flag whether transfer was successful or not */ function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { _updateUserGovIdxTransfer(sender, recipient, amount); _transfer(sender, recipient, amount); _approve(sender, msg.sender, allowance(sender, msg.sender).sub(amount, "ERC20: transfer amount exceeds allowance")); _updateUserFeeInfoTransfer(sender, recipient, amount, userAvgPrices[sender]); return true; } /** * ERC20 modified transfer that also update the avgPrice paid for the recipient and * updates user gov idx * * @param recipient : recipient account * @param amount : value to transfer * @return : flag whether transfer was successful or not */ function transfer(address recipient, uint256 amount) public returns (bool) { _updateUserGovIdxTransfer(msg.sender, recipient, amount); _transfer(msg.sender, recipient, amount); _updateUserFeeInfoTransfer(msg.sender, recipient, amount, userAvgPrices[msg.sender]); return true; } /** * Helper method for transfer and transferFrom, updates recipient gov indexes * * @param _from : sender account * @param _to : recipient account * @param amount : value to transfer */ function _updateUserGovIdxTransfer(address _from, address _to, uint256 amount) internal { address govToken; uint256 govTokenIdx; uint256 sharePerTokenFrom; uint256 shareTo; uint256 balanceTo = balanceOf(_to); for (uint256 i = 0; i < govTokens.length; i++) { govToken = govTokens[i]; if (balanceTo == 0) { usersGovTokensIndexes[govToken][_to] = usersGovTokensIndexes[govToken][_from]; continue; } govTokenIdx = govTokensIndexes[govToken]; // calc 1 idleToken value in gov shares for user `_from` sharePerTokenFrom = govTokenIdx.sub(usersGovTokensIndexes[govToken][_from]); // calc current gov shares (before transfer) for user `_to` shareTo = balanceTo.mul(govTokenIdx.sub(usersGovTokensIndexes[govToken][_to])).div(ONE_18); // user `_to` should have -> shareTo + (sharePerTokenFrom * amount / 1e18) = (balanceTo + amount) * (govTokenIdx - userIdx) / 1e18 // so userIdx = govTokenIdx - ((shareTo * 1e18 + (sharePerTokenFrom * amount)) / (balanceTo + amount)) usersGovTokensIndexes[govToken][_to] = govTokenIdx.sub( shareTo.mul(ONE_18).add(sharePerTokenFrom.mul(amount)).div( balanceTo.add(amount) ) ); } } /** * Get how many gov tokens a user is entitled to (this may not include eventual undistributed tokens) * * @param _usr : user address * @return : array of amounts for each gov token */ function getGovTokensAmounts(address _usr) external view returns (uint256[] memory _amounts) { address govToken; uint256 usrBal = balanceOf(_usr); _amounts = new uint256[](govTokens.length); for (uint256 i = 0; i < _amounts.length; i++) { govToken = govTokens[i]; _amounts[i] = usrBal.mul(govTokensIndexes[govToken].sub(usersGovTokensIndexes[govToken][_usr])).div(ONE_18); } } // external /** * Used to mint IdleTokens, given an underlying amount (eg. DAI). * This method triggers a rebalance of the pools if _skipRebalance is set to false * NOTE: User should 'approve' _amount of tokens before calling mintIdleToken * NOTE 2: this method can be paused * This method use GasTokens of this contract (if present) to get a gas discount * * @param _amount : amount of underlying token to be lended * @param _referral : referral address * @return mintedTokens : amount of IdleTokens minted */ function mintIdleToken(uint256 _amount, bool _skipRebalance, address _referral) external nonReentrant whenNotPaused returns (uint256 mintedTokens) { _minterBlock = keccak256(abi.encodePacked(tx.origin, block.number)); _redeemGovTokens(msg.sender, false); // Get current IdleToken price uint256 idlePrice = _tokenPrice(); // transfer tokens to this contract IERC20(token).safeTransferFrom(msg.sender, address(this), _amount); if (!_skipRebalance) { // lend assets and rebalance the pool if needed _rebalance(); } mintedTokens = _amount.mul(ONE_18).div(idlePrice); _mint(msg.sender, mintedTokens); // Update avg price and/or userNoFeeQty _updateUserFeeInfo(msg.sender, mintedTokens, idlePrice); // Update user idx for each gov tokens _updateUserGovIdx(msg.sender, mintedTokens); if (_referral != address(0)) { emit Referral(_amount, _referral); } } /** * Helper method for mintIdleToken, updates minter gov indexes * * @param _to : minter account * @param _mintedTokens : number of newly minted tokens */ function _updateUserGovIdx(address _to, uint256 _mintedTokens) internal { address govToken; uint256 usrBal = balanceOf(_to); uint256 _govIdx; uint256 _usrIdx; for (uint256 i = 0; i < govTokens.length; i++) { govToken = govTokens[i]; _govIdx = govTokensIndexes[govToken]; _usrIdx = usersGovTokensIndexes[govToken][_to]; // calculate user idx usersGovTokensIndexes[govToken][_to] = usrBal.mul(_usrIdx).add( _mintedTokens.mul(_govIdx.sub(_usrIdx)) ).div(usrBal); } } /** * Here we calc the pool share one can withdraw given the amount of IdleToken they want to burn * * @param _amount : amount of IdleTokens to be burned * @return redeemedTokens : amount of underlying tokens redeemed */ function redeemIdleToken(uint256 _amount) external nonReentrant returns (uint256 redeemedTokens) { _checkMintRedeemSameTx(); _redeemGovTokens(msg.sender, false); uint256 price = _tokenPrice(); uint256 valueToRedeem = _amount.mul(price).div(ONE_18); uint256 balanceUnderlying = IERC20(token).balanceOf(address(this)); uint256 idleSupply = totalSupply(); if (valueToRedeem <= balanceUnderlying) { redeemedTokens = valueToRedeem; } else { address currToken; for (uint256 i = 0; i < allAvailableTokens.length; i++) { currToken = allAvailableTokens[i]; redeemedTokens = redeemedTokens.add( _redeemProtocolTokens( currToken, // _amount * protocolPoolBalance / idleSupply _amount.mul(IERC20(currToken).balanceOf(address(this))).div(idleSupply), // amount to redeem address(this) ) ); } // Get a portion of the eventual unlent balance redeemedTokens = redeemedTokens.add(_amount.mul(balanceUnderlying).div(idleSupply)); } // get eventual performance fee redeemedTokens = _getFee(_amount, redeemedTokens, price); // burn idleTokens _burn(msg.sender, _amount); // send underlying minus fee to msg.sender IERC20(token).safeTransfer(msg.sender, redeemedTokens); } /** * Here we calc the pool share one can withdraw given the amount of IdleToken they want to burn * and send interest-bearing tokens (eg. cDAI/iDAI) directly to the user. * Underlying (eg. DAI) is not redeemed here. * * @param _amount : amount of IdleTokens to be burned */ function redeemInterestBearingTokens(uint256 _amount) external nonReentrant whenPaused { _checkMintRedeemSameTx(); _redeemGovTokens(msg.sender, false); uint256 idleSupply = totalSupply(); address currentToken; for (uint256 i = 0; i < allAvailableTokens.length; i++) { currentToken = allAvailableTokens[i]; IERC20(currentToken).safeTransfer( msg.sender, _amount.mul(IERC20(currentToken).balanceOf(address(this))).div(idleSupply) // amount to redeem ); } // Get a portion of the eventual unlent balance IERC20(token).safeTransfer( msg.sender, _amount.mul(IERC20(token).balanceOf(address(this))).div(idleSupply) // amount to redeem ); _burn(msg.sender, _amount); } /** * Dynamic allocate all the pool across different lending protocols if needed, use gas refund from gasToken * * NOTE: this method can be paused. * msg.sender should approve this contract to spend GST2 tokens before calling * this method * * @return : whether has rebalanced or not */ function rebalanceWithGST() external gasDiscountFrom(msg.sender) returns (bool) { return _rebalance(); } /** * Dynamic allocate all the pool across different lending protocols if needed, * rebalance without params * * NOTE: this method can be paused * * @return : whether has rebalanced or not */ function rebalance() external returns (bool) { return _rebalance(); } /** * Allow any users to set new allocations as long as the new allocation * gives a better avg APR than before * Allocations should be in the format [100000, 0, 0, 0, ...] where length is the same * as lastAllocations variable and the sum of all value should be == 100000 * * This method is not callble if this instance of IdleToken is a risk adjusted instance * NOTE: this method can be paused * * @param _newAllocations : array with new allocations in percentage * @return : whether has rebalanced or not * @return avgApr : the new avg apr after rebalance */ function openRebalance(uint256[] calldata _newAllocations) external whenNotPaused returns (bool, uint256 avgApr) { require(!isRiskAdjusted, "IDLE:NOT_ALLOWED"); uint256 initialAPR = _getAvgAPR(); // Validate and update rebalancer allocations _setAllocations(_newAllocations); bool hasRebalanced = _rebalance(); uint256 newAprAfterRebalance = _getAvgAPR(); require(newAprAfterRebalance > initialAPR, "IDLE:NOT_IMPROV"); return (hasRebalanced, newAprAfterRebalance); } // internal /** * Get current idleToken price based on net asset value and totalSupply * * @return price: value of 1 idleToken in underlying */ function _tokenPrice() internal view returns (uint256 price) { uint256 totSupply = totalSupply(); if (totSupply == 0) { return 10**(tokenDecimals); } address currToken; uint256 totNav = IERC20(token).balanceOf(address(this)).mul(ONE_18); // eventual underlying unlent balance for (uint256 i = 0; i < allAvailableTokens.length; i++) { currToken = allAvailableTokens[i]; totNav = totNav.add( // NAV = price * poolSupply ILendingProtocol(protocolWrappers[currToken]).getPriceInToken().mul( IERC20(currToken).balanceOf(address(this)) ) ); } price = totNav.div(totSupply); // idleToken price in token wei } /** * Dynamic allocate all the pool across different lending protocols if needed * * NOTE: this method can be paused * * @return : whether has rebalanced or not */ function _rebalance() internal whenNotPaused returns (bool) { // check if we need to rebalance by looking at the last allocations submitted by rebalancer uint256[] memory rebalancerLastAllocations = lastRebalancerAllocations; bool areAllocationsEqual = rebalancerLastAllocations.length == lastAllocations.length; if (areAllocationsEqual) { for (uint256 i = 0; i < lastAllocations.length || !areAllocationsEqual; i++) { if (lastAllocations[i] != rebalancerLastAllocations[i]) { areAllocationsEqual = false; break; } } } uint256 balance = IERC20(token).balanceOf(address(this)); uint256 maxUnlentBalance; if (areAllocationsEqual && balance == 0) { return false; } if (balance > 0) { maxUnlentBalance = _getCurrentPoolValue().mul(maxUnlentPerc).div(FULL_ALLOC); if (lastAllocations.length == 0) { // set in storage lastAllocations = rebalancerLastAllocations; } if (balance > maxUnlentBalance) { // mint the difference _mintWithAmounts(allAvailableTokens, _amountsFromAllocations(rebalancerLastAllocations, balance.sub(maxUnlentBalance))); } } if (areAllocationsEqual) { return false; } // Instead of redeeming everything during rebalance we redeem and mint only what needs // to be reallocated // get current allocations in underlying (it does not count unlent underlying) (address[] memory tokenAddresses, uint256[] memory amounts, uint256 totalInUnderlying) = _getCurrentAllocations(); if (balance == 0 && maxUnlentPerc > 0) { totalInUnderlying = totalInUnderlying.sub(_getCurrentPoolValue().mul(maxUnlentPerc).div(FULL_ALLOC)); } // calculate new allocations given the total (not counting unlent balance) uint256[] memory newAmounts = _amountsFromAllocations(rebalancerLastAllocations, totalInUnderlying); (uint256[] memory toMintAllocations, uint256 totalToMint, bool lowLiquidity) = _redeemAllNeeded(tokenAddresses, amounts, newAmounts); // if some protocol has liquidity that we should redeem, we do not update // lastAllocations to force another rebalance next time if (!lowLiquidity) { // Update lastAllocations with rebalancerLastAllocations delete lastAllocations; lastAllocations = rebalancerLastAllocations; } // Do not count `maxUnlentPerc` from balance if (maxUnlentBalance == 0 && maxUnlentPerc > 0) { maxUnlentBalance = _getCurrentPoolValue().mul(maxUnlentPerc).div(FULL_ALLOC); } uint256 totalRedeemd = IERC20(token).balanceOf(address(this)); if (totalRedeemd <= maxUnlentBalance) { return false; } // Do not mint directly using toMintAllocations check with totalRedeemd uint256[] memory tempAllocations = new uint256[](toMintAllocations.length); for (uint256 i = 0; i < toMintAllocations.length; i++) { // Calc what would have been the correct allocations percentage if all was available tempAllocations[i] = toMintAllocations[i].mul(FULL_ALLOC).div(totalToMint); } uint256[] memory partialAmounts = _amountsFromAllocations(tempAllocations, totalRedeemd.sub(maxUnlentBalance)); _mintWithAmounts(allAvailableTokens, partialAmounts); emit Rebalance(msg.sender, totalInUnderlying.add(maxUnlentBalance)); return true; // hasRebalanced } /** * Redeem unclaimed governance tokens and update governance global index and user index if needed * if called during redeem it will send all gov tokens accrued by a user to the user * * @param _to : user address * @param _skipRedeem : flag to choose whether to send gov tokens to user or not */ function _redeemGovTokens(address _to, bool _skipRedeem) internal { if (govTokens.length == 0) { return; } uint256 supply = totalSupply(); uint256 usrBal = balanceOf(_to); address govToken; for (uint256 i = 0; i < govTokens.length; i++) { govToken = govTokens[i]; if (supply > 0) { if (!_skipRedeem) { _redeemGovTokensFromProtocol(govToken); } // get current gov token balance uint256 govBal = IERC20(govToken).balanceOf(address(this)); if (govBal > 0) { // update global index with ratio of govTokens per idleToken govTokensIndexes[govToken] = govTokensIndexes[govToken].add( // check how much gov tokens for each idleToken we gained since last update govBal.sub(govTokensLastBalances[govToken]).mul(ONE_18).div(supply) ); // update global var with current govToken balance govTokensLastBalances[govToken] = govBal; } } if (usrBal > 0) { if (!_skipRedeem) { uint256 usrIndex = usersGovTokensIndexes[govToken][_to]; // update current user index for this gov token usersGovTokensIndexes[govToken][_to] = govTokensIndexes[govToken]; // check if user has accrued something uint256 delta = govTokensIndexes[govToken].sub(usrIndex); if (delta == 0) { continue; } uint256 share = usrBal.mul(delta).div(ONE_18); uint256 bal = IERC20(govToken).balanceOf(address(this)); // To avoid rounding issue if (share > bal) { share = bal; } uint256 feeDue; // no fee for IDLE governance token if (feeAddress != address(0) && fee > 0 && govToken != IDLE) { feeDue = share.mul(fee).div(FULL_ALLOC); // Transfer gov token fee to feeAddress IERC20(govToken).safeTransfer(feeAddress, feeDue); } // Transfer gov token to user IERC20(govToken).safeTransfer(_to, share.sub(feeDue)); // Update last balance govTokensLastBalances[govToken] = IERC20(govToken).balanceOf(address(this)); } } else { // save current index for this gov token usersGovTokensIndexes[govToken][_to] = govTokensIndexes[govToken]; } } } /** * Redeem a specific gov token * * @param _govToken : address of the gov token to redeem */ function _redeemGovTokensFromProtocol(address _govToken) internal { // In case new Gov tokens will be supported this should be updated if (_govToken == COMP || _govToken == IDLE) { address[] memory holders = new address[](1); address[] memory tokens = new address[](1); holders[0] = address(this); if (_govToken == IDLE) { tokens[0] = address(this); IdleController(idleController).claimIdle(holders, tokens); return; } if (cToken != address(0)) { tokens[0] = cToken; Comptroller(CERC20(cToken).comptroller()).claimComp(holders, tokens, false, true); } } } /** * Update userNoFeeQty of a user on transfers and eventually avg price paid for each idle token * userAvgPrice do not consider tokens bought when there was no fee * * @param from : user address of the sender || address(0) on mint * @param usr : user that should have balance update * @param qty : new amount deposited / transferred, in idleToken * @param price : curr idleToken price in underlying */ function _updateUserFeeInfoTransfer(address from, address usr, uint256 qty, uint256 price) private { uint256 userNoFeeQtyFrom = userNoFeeQty[from]; if (userNoFeeQtyFrom >= qty) { userNoFeeQty[from] = userNoFeeQtyFrom.sub(qty); userNoFeeQty[usr] = userNoFeeQty[usr].add(qty); // No avg price update needed return; } // nofeeQty not counted uint256 oldBalance = balanceOf(usr).sub(qty).sub(userNoFeeQty[usr]); uint256 newQty = qty.sub(userNoFeeQtyFrom); // (avgPrice * oldBalance) + (currPrice * newQty)) / totBalance userAvgPrices[usr] = userAvgPrices[usr].mul(oldBalance).add(price.mul(newQty)).div(oldBalance.add(newQty)); // update no fee quantities userNoFeeQty[from] = 0; userNoFeeQty[usr] = userNoFeeQty[usr].add(userNoFeeQtyFrom); } /** * Update userNoFeeQty of a user on deposits and eventually avg price paid for each idle token * userAvgPrice do not consider tokens bought when there was no fee * * @param usr : user that should have balance update * @param qty : new amount deposited / transferred, in idleToken * @param price : curr idleToken price in underlying */ function _updateUserFeeInfo(address usr, uint256 qty, uint256 price) private { if (fee == 0) { // on deposits with 0 fee userNoFeeQty[usr] = userNoFeeQty[usr].add(qty); return; } // on deposits with fee uint256 totBalance = balanceOf(usr).sub(userNoFeeQty[usr]); // noFeeQty should not be counted here // (avgPrice * oldBalance) + (currPrice * newQty)) / totBalance userAvgPrices[usr] = userAvgPrices[usr].mul(totBalance.sub(qty)).add(price.mul(qty)).div(totBalance); } /** * Calculate fee and send them to feeAddress * * @param amount : in idleTokens * @param redeemed : in underlying * @param currPrice : current idleToken price * @return : net value in underlying */ function _getFee(uint256 amount, uint256 redeemed, uint256 currPrice) internal returns (uint256) { uint256 noFeeQty = userNoFeeQty[msg.sender]; // in idleTokens bool hasEnoughNoFeeQty = noFeeQty >= amount; if (fee == 0 || hasEnoughNoFeeQty) { userNoFeeQty[msg.sender] = hasEnoughNoFeeQty ? noFeeQty.sub(amount) : 0; return redeemed; } userNoFeeQty[msg.sender] = 0; uint256 elegibleGains = currPrice < userAvgPrices[msg.sender] ? 0 : amount.sub(noFeeQty).mul(currPrice.sub(userAvgPrices[msg.sender])).div(ONE_18); // in underlyings uint256 feeDue = elegibleGains.mul(fee).div(FULL_ALLOC); IERC20(token).safeTransfer(feeAddress, feeDue); return redeemed.sub(feeDue); } /** * Mint specific amounts of protocols tokens * * @param tokenAddresses : array of protocol tokens * @param protocolAmounts : array of amounts to be minted * @return : net value in underlying */ function _mintWithAmounts(address[] memory tokenAddresses, uint256[] memory protocolAmounts) internal { // mint for each protocol and update currentTokensUsed uint256 currAmount; address protWrapper; for (uint256 i = 0; i < protocolAmounts.length; i++) { currAmount = protocolAmounts[i]; if (currAmount == 0) { continue; } protWrapper = protocolWrappers[tokenAddresses[i]]; // Transfer _amount underlying token (eg. DAI) to protWrapper IERC20(token).safeTransfer(protWrapper, currAmount); ILendingProtocol(protWrapper).mint(); } } /** * Calculate amounts from percentage allocations (100000 => 100%) * * @param allocations : array of protocol allocations in percentage * @param total : total amount * @return : array with amounts */ function _amountsFromAllocations(uint256[] memory allocations, uint256 total) internal pure returns (uint256[] memory newAmounts) { newAmounts = new uint256[](allocations.length); uint256 currBalance; uint256 allocatedBalance; for (uint256 i = 0; i < allocations.length; i++) { if (i == allocations.length - 1) { newAmounts[i] = total.sub(allocatedBalance); } else { currBalance = total.mul(allocations[i]).div(FULL_ALLOC); allocatedBalance = allocatedBalance.add(currBalance); newAmounts[i] = currBalance; } } return newAmounts; } /** * Redeem all underlying needed from each protocol * * @param tokenAddresses : array of protocol tokens addresses * @param amounts : array with current allocations in underlying * @param newAmounts : array with new allocations in underlying * @return toMintAllocations : array with amounts to be minted * @return totalToMint : total amount that needs to be minted */ function _redeemAllNeeded( address[] memory tokenAddresses, uint256[] memory amounts, uint256[] memory newAmounts ) internal returns ( uint256[] memory toMintAllocations, uint256 totalToMint, bool lowLiquidity ) { toMintAllocations = new uint256[](amounts.length); ILendingProtocol protocol; uint256 currAmount; uint256 newAmount; address currToken; // check the difference between amounts and newAmounts for (uint256 i = 0; i < amounts.length; i++) { currToken = tokenAddresses[i]; newAmount = newAmounts[i]; currAmount = amounts[i]; protocol = ILendingProtocol(protocolWrappers[currToken]); if (currAmount > newAmount) { uint256 toRedeem = currAmount.sub(newAmount); uint256 availableLiquidity = protocol.availableLiquidity(); if (availableLiquidity < toRedeem) { lowLiquidity = true; toRedeem = availableLiquidity; } // redeem the difference _redeemProtocolTokens( currToken, // convert amount from underlying to protocol token toRedeem.mul(ONE_18).div(protocol.getPriceInToken()), address(this) // tokens are now in this contract ); } else { toMintAllocations[i] = newAmount.sub(currAmount); totalToMint = totalToMint.add(toMintAllocations[i]); } } } /** * Get the contract balance of every protocol currently used * * @return tokenAddresses : array with all token addresses used, * eg [cTokenAddress, iTokenAddress] * @return amounts : array with all amounts for each protocol in order, * eg [amountCompoundInUnderlying, amountFulcrumInUnderlying] * @return total : total AUM in underlying */ function _getCurrentAllocations() internal view returns (address[] memory tokenAddresses, uint256[] memory amounts, uint256 total) { // Get balance of every protocol implemented tokenAddresses = new address[](allAvailableTokens.length); amounts = new uint256[](allAvailableTokens.length); address currentToken; uint256 currTokenPrice; for (uint256 i = 0; i < allAvailableTokens.length; i++) { currentToken = allAvailableTokens[i]; tokenAddresses[i] = currentToken; currTokenPrice = ILendingProtocol(protocolWrappers[currentToken]).getPriceInToken(); amounts[i] = currTokenPrice.mul( IERC20(currentToken).balanceOf(address(this)) ).div(ONE_18); total = total.add(amounts[i]); } // return addresses and respective amounts in underlying return (tokenAddresses, amounts, total); } /** * Get the current pool value in underlying * * @return total : total AUM in underlying */ function _getCurrentPoolValue() internal view returns (uint256 total) { // Get balance of every protocol implemented address currentToken; for (uint256 i = 0; i < allAvailableTokens.length; i++) { currentToken = allAvailableTokens[i]; total = total.add(ILendingProtocol(protocolWrappers[currentToken]).getPriceInToken().mul( IERC20(currentToken).balanceOf(address(this)) ).div(ONE_18)); } // add unlent balance total = total.add(IERC20(token).balanceOf(address(this))); } /** * Check that no mint has been made in the same block from the same EOA */ function _checkMintRedeemSameTx() private view { require(keccak256(abi.encodePacked(tx.origin, block.number)) != _minterBlock, "IDLE:REENTR"); } // ILendingProtocols calls /** * Redeem underlying tokens through protocol wrapper * * @param _amount : amount of `_token` to redeem * @param _token : protocol token address * @param _account : should be msg.sender when rebalancing and final user when redeeming * @return tokens : new tokens minted */ function _redeemProtocolTokens(address _token, uint256 _amount, address _account) internal returns (uint256 tokens) { if (_amount == 0) { return tokens; } // Transfer _amount of _protocolToken (eg. cDAI) to _wrapperAddr IERC20(_token).safeTransfer(protocolWrappers[_token], _amount); tokens = ILendingProtocol(protocolWrappers[_token]).redeem(_account); } }
File 8 of 11: CErc20Delegator
pragma solidity ^0.5.16; import "./CTokenInterfaces.sol"; /** * @title Compound's CErc20Delegator Contract * @notice CTokens which wrap an EIP-20 underlying and delegate to an implementation * @author Compound */ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterface { /** * @notice Construct a new money market * @param underlying_ The address of the underlying asset * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ ERC-20 name of this token * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token * @param admin_ Address of the administrator of this token * @param implementation_ The address of the implementation the contract delegates to * @param becomeImplementationData The encoded args for becomeImplementation */ constructor(address underlying_, ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_, address payable admin_, address implementation_, bytes memory becomeImplementationData) public { // Creator of the contract is admin during initialization admin = msg.sender; // First delegate gets to initialize the delegator (i.e. storage contract) delegateTo(implementation_, abi.encodeWithSignature("initialize(address,address,address,uint256,string,string,uint8)", underlying_, comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_)); // New implementations always get set via the settor (post-initialize) _setImplementation(implementation_, false, becomeImplementationData); // Set the proper admin now that initialization is done admin = admin_; } /** * @notice Called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation */ function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public { require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin"); if (allowResign) { delegateToImplementation(abi.encodeWithSignature("_resignImplementation()")); } address oldImplementation = implementation; implementation = implementation_; delegateToImplementation(abi.encodeWithSignature("_becomeImplementation(bytes)", becomeImplementationData)); emit NewImplementation(oldImplementation, implementation); } /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function mint(uint mintAmount) external returns (uint) { mintAmount; // Shh delegateAndReturn(); } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeem(uint redeemTokens) external returns (uint) { redeemTokens; // Shh delegateAndReturn(); } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemUnderlying(uint redeemAmount) external returns (uint) { redeemAmount; // Shh delegateAndReturn(); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrow(uint borrowAmount) external returns (uint) { borrowAmount; // Shh delegateAndReturn(); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrow(uint repayAmount) external returns (uint) { repayAmount; // Shh delegateAndReturn(); } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint) { borrower; repayAmount; // Shh delegateAndReturn(); } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint) { borrower; repayAmount; cTokenCollateral; // Shh delegateAndReturn(); } /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transfer(address dst, uint amount) external returns (bool) { dst; amount; // Shh delegateAndReturn(); } /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferFrom(address src, address dst, uint256 amount) external returns (bool) { src; dst; amount; // Shh delegateAndReturn(); } /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved (-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool) { spender; amount; // Shh delegateAndReturn(); } /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) external view returns (uint) { owner; spender; // Shh delegateToViewAndReturn(); } /** * @notice Get the token balance of the `owner` * @param owner The address of the account to query * @return The number of tokens owned by `owner` */ function balanceOf(address owner) external view returns (uint) { owner; // Shh delegateToViewAndReturn(); } /** * @notice Get the underlying balance of the `owner` * @dev This also accrues interest in a transaction * @param owner The address of the account to query * @return The amount of underlying owned by `owner` */ function balanceOfUnderlying(address owner) external returns (uint) { owner; // Shh delegateAndReturn(); } /** * @notice Get a snapshot of the account's balances, and the cached exchange rate * @dev This is used by comptroller to more efficiently perform liquidity checks. * @param account Address of the account to snapshot * @return (possible error, token balance, borrow balance, exchange rate mantissa) */ function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint) { account; // Shh delegateToViewAndReturn(); } /** * @notice Returns the current per-block borrow interest rate for this cToken * @return The borrow interest rate per block, scaled by 1e18 */ function borrowRatePerBlock() external view returns (uint) { delegateToViewAndReturn(); } /** * @notice Returns the current per-block supply interest rate for this cToken * @return The supply interest rate per block, scaled by 1e18 */ function supplyRatePerBlock() external view returns (uint) { delegateToViewAndReturn(); } /** * @notice Returns the current total borrows plus accrued interest * @return The total borrows with interest */ function totalBorrowsCurrent() external returns (uint) { delegateAndReturn(); } /** * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex * @param account The address whose balance should be calculated after updating borrowIndex * @return The calculated balance */ function borrowBalanceCurrent(address account) external returns (uint) { account; // Shh delegateAndReturn(); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return The calculated balance */ function borrowBalanceStored(address account) public view returns (uint) { account; // Shh delegateToViewAndReturn(); } /** * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateCurrent() public returns (uint) { delegateAndReturn(); } /** * @notice Calculates the exchange rate from the underlying to the CToken * @dev This function does not accrue interest before calculating the exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateStored() public view returns (uint) { delegateToViewAndReturn(); } /** * @notice Get cash balance of this cToken in the underlying asset * @return The quantity of underlying asset owned by this contract */ function getCash() external view returns (uint) { delegateToViewAndReturn(); } /** * @notice Applies accrued interest to total borrows and reserves. * @dev This calculates interest accrued from the last checkpointed block * up to the current block and writes new checkpoint to storage. */ function accrueInterest() public returns (uint) { delegateAndReturn(); } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Will fail unless called by another cToken during the process of liquidation. * Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter. * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of cTokens to seize * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint) { liquidator; borrower; seizeTokens; // Shh delegateAndReturn(); } /*** Admin Functions ***/ /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) { newPendingAdmin; // Shh delegateAndReturn(); } /** * @notice Sets a new comptroller for the market * @dev Admin function to set a new comptroller * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setComptroller(ComptrollerInterface newComptroller) public returns (uint) { newComptroller; // Shh delegateAndReturn(); } /** * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh * @dev Admin function to accrue interest and set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint) { newReserveFactorMantissa; // Shh delegateAndReturn(); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptAdmin() external returns (uint) { delegateAndReturn(); } /** * @notice Accrues interest and adds reserves by transferring from admin * @param addAmount Amount of reserves to add * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReserves(uint addAmount) external returns (uint) { addAmount; // Shh delegateAndReturn(); } /** * @notice Accrues interest and reduces reserves by transferring to admin * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReserves(uint reduceAmount) external returns (uint) { reduceAmount; // Shh delegateAndReturn(); } /** * @notice Accrues interest and updates the interest rate model using _setInterestRateModelFresh * @dev Admin function to accrue interest and update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint) { newInterestRateModel; // Shh delegateAndReturn(); } /** * @notice Internal method to delegate execution to another contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * @param callee The contract to delegatecall * @param data The raw data to delegatecall * @return The returned bytes from the delegatecall */ function delegateTo(address callee, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returnData) = callee.delegatecall(data); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize) } } return returnData; } /** * @notice Delegates execution to the implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * @param data The raw data to delegatecall * @return The returned bytes from the delegatecall */ function delegateToImplementation(bytes memory data) public returns (bytes memory) { return delegateTo(implementation, data); } /** * @notice Delegates execution to an implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop. * @param data The raw data to delegatecall * @return The returned bytes from the delegatecall */ function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { (bool success, bytes memory returnData) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", data)); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize) } } return abi.decode(returnData, (bytes)); } function delegateToViewAndReturn() private view returns (bytes memory) { (bool success, ) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", msg.data)); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize) switch success case 0 { revert(free_mem_ptr, returndatasize) } default { return(add(free_mem_ptr, 0x40), returndatasize) } } } function delegateAndReturn() private returns (bytes memory) { (bool success, ) = implementation.delegatecall(msg.data); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize) switch success case 0 { revert(free_mem_ptr, returndatasize) } default { return(free_mem_ptr, returndatasize) } } } /** * @notice Delegates execution to an implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts */ function () external payable { require(msg.value == 0,"CErc20Delegator:fallback: cannot send value to fallback"); // delegate all other functions to current implementation delegateAndReturn(); } } pragma solidity ^0.5.16; import "./ComptrollerInterface.sol"; import "./InterestRateModel.sol"; contract CTokenStorage { /** * @dev Guard variable for re-entrancy checks */ bool internal _notEntered; /** * @notice EIP-20 token name for this token */ string public name; /** * @notice EIP-20 token symbol for this token */ string public symbol; /** * @notice EIP-20 token decimals for this token */ uint8 public decimals; /** * @notice Maximum borrow rate that can ever be applied (.0005% / block) */ uint internal constant borrowRateMaxMantissa = 0.0005e16; /** * @notice Maximum fraction of interest that can be set aside for reserves */ uint internal constant reserveFactorMaxMantissa = 1e18; /** * @notice Administrator for this contract */ address payable public admin; /** * @notice Pending administrator for this contract */ address payable public pendingAdmin; /** * @notice Contract which oversees inter-cToken operations */ ComptrollerInterface public comptroller; /** * @notice Model which tells what the current interest rate should be */ InterestRateModel public interestRateModel; /** * @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0) */ uint internal initialExchangeRateMantissa; /** * @notice Fraction of interest currently set aside for reserves */ uint public reserveFactorMantissa; /** * @notice Block number that interest was last accrued at */ uint public accrualBlockNumber; /** * @notice Accumulator of the total earned interest rate since the opening of the market */ uint public borrowIndex; /** * @notice Total amount of outstanding borrows of the underlying in this market */ uint public totalBorrows; /** * @notice Total amount of reserves of the underlying held in this market */ uint public totalReserves; /** * @notice Total number of tokens in circulation */ uint public totalSupply; /** * @notice Official record of token balances for each account */ mapping (address => uint) internal accountTokens; /** * @notice Approved token transfer amounts on behalf of others */ mapping (address => mapping (address => uint)) internal transferAllowances; /** * @notice Container for borrow balance information * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action * @member interestIndex Global borrowIndex as of the most recent balance-changing action */ struct BorrowSnapshot { uint principal; uint interestIndex; } /** * @notice Mapping of account addresses to outstanding borrow balances */ mapping(address => BorrowSnapshot) internal accountBorrows; } contract CTokenInterface is CTokenStorage { /** * @notice Indicator that this is a CToken contract (for inspection) */ bool public constant isCToken = true; /*** Market Events ***/ /** * @notice Event emitted when interest is accrued */ event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); /** * @notice Event emitted when tokens are minted */ event Mint(address minter, uint mintAmount, uint mintTokens); /** * @notice Event emitted when tokens are redeemed */ event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); /** * @notice Event emitted when underlying is borrowed */ event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows); /** * @notice Event emitted when a borrow is repaid */ event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows); /** * @notice Event emitted when a borrow is liquidated */ event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens); /*** Admin Events ***/ /** * @notice Event emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Event emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); /** * @notice Event emitted when comptroller is changed */ event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller); /** * @notice Event emitted when interestRateModel is changed */ event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel); /** * @notice Event emitted when the reserve factor is changed */ event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa); /** * @notice Event emitted when the reserves are added */ event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves); /** * @notice Event emitted when the reserves are reduced */ event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves); /** * @notice EIP20 Transfer event */ event Transfer(address indexed from, address indexed to, uint amount); /** * @notice EIP20 Approval event */ event Approval(address indexed owner, address indexed spender, uint amount); /** * @notice Failure event */ event Failure(uint error, uint info, uint detail); /*** User Interface ***/ function transfer(address dst, uint amount) external returns (bool); function transferFrom(address src, address dst, uint amount) external returns (bool); function approve(address spender, uint amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint); function balanceOf(address owner) external view returns (uint); function balanceOfUnderlying(address owner) external returns (uint); function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint); function borrowRatePerBlock() external view returns (uint); function supplyRatePerBlock() external view returns (uint); function totalBorrowsCurrent() external returns (uint); function borrowBalanceCurrent(address account) external returns (uint); function borrowBalanceStored(address account) public view returns (uint); function exchangeRateCurrent() public returns (uint); function exchangeRateStored() public view returns (uint); function getCash() external view returns (uint); function accrueInterest() public returns (uint); function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint); /*** Admin Functions ***/ function _setPendingAdmin(address payable newPendingAdmin) external returns (uint); function _acceptAdmin() external returns (uint); function _setComptroller(ComptrollerInterface newComptroller) public returns (uint); function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint); function _reduceReserves(uint reduceAmount) external returns (uint); function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint); } contract CErc20Storage { /** * @notice Underlying asset for this CToken */ address public underlying; } contract CErc20Interface is CErc20Storage { /*** User Interface ***/ function mint(uint mintAmount) external returns (uint); function redeem(uint redeemTokens) external returns (uint); function redeemUnderlying(uint redeemAmount) external returns (uint); function borrow(uint borrowAmount) external returns (uint); function repayBorrow(uint repayAmount) external returns (uint); function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint); function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint); /*** Admin Functions ***/ function _addReserves(uint addAmount) external returns (uint); } contract CDelegationStorage { /** * @notice Implementation address for this contract */ address public implementation; } contract CDelegatorInterface is CDelegationStorage { /** * @notice Emitted when implementation is changed */ event NewImplementation(address oldImplementation, address newImplementation); /** * @notice Called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation */ function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public; } contract CDelegateInterface is CDelegationStorage { /** * @notice Called by the delegator on a delegate to initialize it for duty * @dev Should revert if any issues arise which make it unfit for delegation * @param data The encoded bytes data for any initialization */ function _becomeImplementation(bytes memory data) public; /** * @notice Called by the delegator on a delegate to forfeit its responsibility */ function _resignImplementation() public; } pragma solidity ^0.5.16; contract ComptrollerInterface { /// @notice Indicator that this is a Comptroller contract (for inspection) bool public constant isComptroller = true; /*** Assets You Are In ***/ function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); function exitMarket(address cToken) external returns (uint); /*** Policy Hooks ***/ function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint); function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external; function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint); function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external; function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint); function borrowVerify(address cToken, address borrower, uint borrowAmount) external; function repayBorrowAllowed( address cToken, address payer, address borrower, uint repayAmount) external returns (uint); function repayBorrowVerify( address cToken, address payer, address borrower, uint repayAmount, uint borrowerIndex) external; function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint repayAmount) external returns (uint); function liquidateBorrowVerify( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint repayAmount, uint seizeTokens) external; function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint seizeTokens) external returns (uint); function seizeVerify( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint seizeTokens) external; function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint); function transferVerify(address cToken, address src, address dst, uint transferTokens) external; /*** Liquidity/Liquidation Calculations ***/ function liquidateCalculateSeizeTokens( address cTokenBorrowed, address cTokenCollateral, uint repayAmount) external view returns (uint, uint); } pragma solidity ^0.5.16; /** * @title Compound's InterestRateModel Interface * @author Compound */ contract InterestRateModel { /// @notice Indicator that this is an InterestRateModel contract (for inspection) bool public constant isInterestRateModel = true; /** * @notice Calculates the current borrow interest rate per block * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amnount of reserves the market has * @return The borrow rate per block (as a percentage, and scaled by 1e18) */ function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint); /** * @notice Calculates the current supply interest rate per block * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amnount of reserves the market has * @param reserveFactorMantissa The current reserve factor the market has * @return The supply rate per block (as a percentage, and scaled by 1e18) */ function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint); }
File 9 of 11: IdleCompoundV2
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol pragma solidity ^0.5.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see {ERC20Detailed}. */ 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); } // File: @openzeppelin/contracts/math/SafeMath.sol pragma solidity ^0.5.0; /** * @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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File: @openzeppelin/contracts/utils/Address.sol pragma solidity ^0.5.5; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing 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. */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. // 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 != 0x0 && codehash != accountHash); } /** * @dev Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * _Available since v2.4.0._ */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } /** * @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]. * * _Available since v2.4.0._ */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-call-value (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol pragma solidity ^0.5.0; /** * @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 ERC20;` 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 { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length 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 safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length 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"); } } } // File: @openzeppelin/contracts/GSN/Context.sol pragma solidity ^0.5.0; /* * @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. */ contract Context { // Empty internal constructor, to prevent people from mistakenly deploying // an instance of this contract, which should be used via inheritance. constructor () internal { } // solhint-disable-previous-line no-empty-blocks function _msgSender() internal view returns (address payable) { return msg.sender; } function _msgData() internal view returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File: @openzeppelin/contracts/ownership/Ownable.sol pragma solidity ^0.5.0; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { _owner = _msgSender(); emit OwnershipTransferred(address(0), _owner); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } /** * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { return _msgSender() == _owner; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/interfaces/CERC20.sol pragma solidity 0.5.16; interface CERC20 { function mint(uint256 mintAmount) external returns (uint256); function comptroller() external view returns (address); function redeem(uint256 redeemTokens) external returns (uint256); function exchangeRateStored() external view returns (uint256); function supplyRatePerBlock() external view returns (uint256); function borrowRatePerBlock() external view returns (uint256); function totalReserves() external view returns (uint256); function getCash() external view returns (uint256); function totalBorrows() external view returns (uint256); function reserveFactorMantissa() external view returns (uint256); function interestRateModel() external view returns (address); } // File: contracts/interfaces/ILendingProtocol.sol pragma solidity 0.5.16; interface ILendingProtocol { function mint() external returns (uint256); function redeem(address account) external returns (uint256); function nextSupplyRate(uint256 amount) external view returns (uint256); function nextSupplyRateWithParams(uint256[] calldata params) external view returns (uint256); function getAPR() external view returns (uint256); function getPriceInToken() external view returns (uint256); function token() external view returns (address); function underlying() external view returns (address); function availableLiquidity() external view returns (uint256); } // File: contracts/interfaces/WhitePaperInterestRateModel.sol pragma solidity 0.5.16; interface WhitePaperInterestRateModel { function getBorrowRate(uint256 cash, uint256 borrows, uint256 _reserves) external view returns (uint256, uint256); function getSupplyRate(uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa) external view returns (uint256); function multiplier() external view returns (uint256); function baseRate() external view returns (uint256); function blocksPerYear() external view returns (uint256); function dsrPerBlock() external view returns (uint256); } // File: contracts/wrappers/IdleCompoundV2.sol /** * @title: Compound wrapper * @summary: Used for interacting with Compound. Has * a common interface with all other protocol wrappers. * This contract holds assets only during a tx, after tx it should be empty * @author: Idle Labs Inc., idle.finance */ pragma solidity 0.5.16; contract IdleCompoundV2 is ILendingProtocol, Ownable { using SafeERC20 for IERC20; using SafeMath for uint256; // protocol token (cToken) address address public token; // underlying token (token eg DAI) address address public underlying; address public idleToken; uint256 public blocksPerYear; /** * @param _token : cToken address * @param _underlying : underlying token (eg DAI) address */ constructor(address _token, address _underlying) public { require(_token != address(0) && _underlying != address(0), 'COMP: some addr is 0'); token = _token; underlying = _underlying; blocksPerYear = 2371428; IERC20(_underlying).safeApprove(_token, uint256(-1)); } /** * Throws if called by any account other than IdleToken contract. */ modifier onlyIdle() { require(msg.sender == idleToken, "Ownable: caller is not IdleToken"); _; } // onlyOwner /** * sets idleToken address * NOTE: can be called only once. It's not on the constructor because we are deploying this contract * after the IdleToken contract * @param _idleToken : idleToken address */ function setIdleToken(address _idleToken) external onlyOwner { require(idleToken == address(0), "idleToken addr already set"); require(_idleToken != address(0), "_idleToken addr is 0"); idleToken = _idleToken; } /** * sets blocksPerYear address * * @param _blocksPerYear : avg blocks per year */ function setBlocksPerYear(uint256 _blocksPerYear) external onlyOwner { require(_blocksPerYear != 0, "_blocksPerYear is 0"); blocksPerYear = _blocksPerYear; } // end onlyOwner /** * Calculate next supply rate for Compound, given an `_amount` supplied (last array param) * and all other params supplied. * * @param params : array with all params needed for calculation * @return : yearly net rate */ function nextSupplyRateWithParams(uint256[] calldata params) external view returns (uint256) { CERC20 cToken = CERC20(token); WhitePaperInterestRateModel white = WhitePaperInterestRateModel(cToken.interestRateModel()); uint256 ratePerBlock = white.getSupplyRate( params[1].add(params[5]), params[0], params[2], params[3] ); return ratePerBlock.mul(params[4]).mul(100); } /** * Calculate next supply rate for Compound, given an `_amount` supplied * * @param _amount : new underlying amount supplied (eg DAI) * @return : yearly net rate */ function nextSupplyRate(uint256 _amount) external view returns (uint256) { CERC20 cToken = CERC20(token); WhitePaperInterestRateModel white = WhitePaperInterestRateModel(cToken.interestRateModel()); uint256 ratePerBlock = white.getSupplyRate( cToken.getCash().add(_amount), cToken.totalBorrows(), cToken.totalReserves(), cToken.reserveFactorMantissa() ); return ratePerBlock.mul(blocksPerYear).mul(100); } /** * @return current price of cToken in underlying */ function getPriceInToken() external view returns (uint256) { return CERC20(token).exchangeRateStored(); } /** * @return apr : current yearly net rate */ function getAPR() external view returns (uint256 apr) { CERC20 cToken = CERC20(token); uint256 cRate = cToken.supplyRatePerBlock(); // interest % per block apr = cRate.mul(blocksPerYear).mul(100); } /** * Gets all underlying tokens in this contract and mints cTokens * tokens are then transferred to msg.sender * NOTE: underlying tokens needs to be sended here before calling this * * @return iTokens minted */ function mint() external onlyIdle returns (uint256 cTokens) { uint256 balance = IERC20(underlying).balanceOf(address(this)); if (balance == 0) { return cTokens; } // get a handle for the corresponding cToken contract CERC20 _cToken = CERC20(token); // mint the cTokens and assert there is no error require(_cToken.mint(balance) == 0, "Error minting cTokens"); // cTokens are now in this contract cTokens = IERC20(token).balanceOf(address(this)); // transfer them to the caller IERC20(token).safeTransfer(msg.sender, cTokens); } /** * Gets all cTokens in this contract and redeems underlying tokens. * underlying tokens are then transferred to `_account` * NOTE: iTokens needs to be sended here before calling this * * @return underlying tokens redeemd */ function redeem(address _account) external onlyIdle returns (uint256 tokens) { // Funds needs to be sended here before calling this CERC20 _cToken = CERC20(token); IERC20 _underlying = IERC20(underlying); // redeem all underlying sent in this contract require(_cToken.redeem(IERC20(token).balanceOf(address(this))) == 0, "Error redeeming cTokens"); tokens = _underlying.balanceOf(address(this)); _underlying.safeTransfer(_account, tokens); } function availableLiquidity() external view returns (uint256) { return CERC20(token).getCash(); } }
File 10 of 11: AToken
pragma solidity ^0.5.0; /** * @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) { require(b <= a, "SafeMath: subtraction overflow"); 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-solidity/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) { // Solidity only automatically asserts when dividing by 0 require(b > 0, "SafeMath: division by zero"); 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) { require(b != 0, "SafeMath: modulo by zero"); return a % b; } } /** * @title WadRayMath library * @author Aave * @dev Provides mul and div function for wads (decimal numbers with 18 digits precision) and rays (decimals with 27 digits) **/ library WadRayMath { using SafeMath for uint256; uint256 internal constant WAD = 1e18; uint256 internal constant halfWAD = WAD / 2; uint256 internal constant RAY = 1e27; uint256 internal constant halfRAY = RAY / 2; uint256 internal constant WAD_RAY_RATIO = 1e9; /** * @return one ray, 1e27 **/ function ray() internal pure returns (uint256) { return RAY; } /** * @return one wad, 1e18 **/ function wad() internal pure returns (uint256) { return WAD; } /** * @return half ray, 1e27/2 **/ function halfRay() internal pure returns (uint256) { return halfRAY; } /** * @return half ray, 1e18/2 **/ function halfWad() internal pure returns (uint256) { return halfWAD; } /** * @dev multiplies two wad, rounding half up to the nearest wad * @param a wad * @param b wad * @return the result of a*b, in wad **/ function wadMul(uint256 a, uint256 b) internal pure returns (uint256) { return halfWAD.add(a.mul(b)).div(WAD); } /** * @dev divides two wad, rounding half up to the nearest wad * @param a wad * @param b wad * @return the result of a/b, in wad **/ function wadDiv(uint256 a, uint256 b) internal pure returns (uint256) { uint256 halfB = b / 2; return halfB.add(a.mul(WAD)).div(b); } /** * @dev multiplies two ray, rounding half up to the nearest ray * @param a ray * @param b ray * @return the result of a*b, in ray **/ function rayMul(uint256 a, uint256 b) internal pure returns (uint256) { return halfRAY.add(a.mul(b)).div(RAY); } /** * @dev divides two ray, rounding half up to the nearest ray * @param a ray * @param b ray * @return the result of a/b, in ray **/ function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) { uint256 halfB = b / 2; return halfB.add(a.mul(RAY)).div(b); } /** * @dev casts ray down to wad * @param a ray * @return a casted to wad, rounded half up to the nearest wad **/ function rayToWad(uint256 a) internal pure returns (uint256) { uint256 halfRatio = WAD_RAY_RATIO / 2; return halfRatio.add(a).div(WAD_RAY_RATIO); } /** * @dev convert wad up to ray * @param a wad * @return a converted in ray **/ function wadToRay(uint256 a) internal pure returns (uint256) { return a.mul(WAD_RAY_RATIO); } /** * @dev calculates base^exp. The code uses the ModExp precompile * @return base^exp, in ray */ //solium-disable-next-line function rayPow(uint256 x, uint256 n) internal pure returns (uint256 z) { z = n % 2 != 0 ? x : RAY; for (n /= 2; n != 0; n /= 2) { x = rayMul(x, x); if (n % 2 != 0) { z = rayMul(z, x); } } } } /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier * available, which can be aplied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. */ contract ReentrancyGuard { /// @dev counter to allow mutex lock with only one SSTORE operation uint256 private _guardCounter; constructor () internal { // The counter starts at one to prevent changing it from zero to a non-zero // value, which is a more expensive operation. _guardCounter = 1; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { _guardCounter += 1; uint256 localCounter = _guardCounter; _; require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call"); } } /** * @dev Collection of functions related to the address type, */ library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing a contract. * * > It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see `ERC20Detailed`. */ 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. * * > 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); } /** * @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 `ERC20Mintable`. * * *For a detailed writeup see our guide [How to implement supply * mechanisms](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).* * * 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 IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; /** * @dev See `IERC20.totalSupply`. */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev See `IERC20.balanceOf`. */ function balanceOf(address account) public 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`. */ function transfer(address recipient, uint256 amount) public returns (bool) { _transfer(msg.sender, recipient, amount); return true; } /** * @dev See `IERC20.allowance`. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowances[owner][spender]; } /** * @dev See `IERC20.approve`. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public returns (bool) { _approve(msg.sender, spender, value); 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 `value`. * - the caller must have allowance for `sender`'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { _transfer(sender, recipient, amount); _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount)); return true; } /** * @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. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][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`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue)); 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, uint256 amount) internal { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _balances[sender] = _balances[sender].sub(amount); _balances[recipient] = _balances[recipient].add(amount); 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 { require(account != address(0), "ERC20: mint to the zero address"); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destoys `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 value) internal { require(account != address(0), "ERC20: burn from the zero address"); _totalSupply = _totalSupply.sub(value); _balances[account] = _balances[account].sub(value); emit Transfer(account, address(0), value); } /** * @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, uint256 value) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = value; emit Approval(owner, spender, value); } /** * @dev Destoys `amount` tokens from `account`.`amount` is then deducted * from the caller's allowance. * * See `_burn` and `_approve`. */ function _burnFrom(address account, uint256 amount) internal { _burn(account, amount); _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount)); } } /** * @dev Optional functions from the ERC20 standard. */ contract ERC20Detailed is IERC20 { string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of * these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol, uint8 decimals) public { _name = name; _symbol = symbol; _decimals = decimals; } /** * @dev Returns the name of the token. */ function name() public 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 _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. * * > Note that 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 _decimals; } } /** * @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 ERC20;` 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 { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length 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 safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length 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"); } } } /** * @title VersionedInitializable * * @dev Helper contract to support initializer functions. To use it, replace * the constructor with a function that has the `initializer` modifier. * WARNING: Unlike constructors, initializer functions must be manually * invoked. This applies both to deploying an Initializable contract, as well * as extending an Initializable contract via inheritance. * WARNING: When used with inheritance, manual care must be taken to not invoke * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. * * @author Aave, inspired by the OpenZeppelin Initializable contract */ contract VersionedInitializable { /** * @dev Indicates that the contract has been initialized. */ uint256 private lastInitializedRevision = 0; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private initializing; /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { uint256 revision = getRevision(); require(initializing || isConstructor() || revision > lastInitializedRevision, "Contract instance has already been initialized"); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; lastInitializedRevision = revision; } _; if (isTopLevelCall) { initializing = false; } } /// @dev returns the revision number of the contract. /// Needs to be defined in the inherited class as a constant. function getRevision() internal pure returns(uint256); /// @dev Returns true if and only if the function is running in the constructor function isConstructor() private view returns (bool) { // extcodesize checks the size of the code stored in an address, and // address returns the current address. Since the code is still not // deployed when running a constructor, any checks on its code size will // yield zero, making it an effective way to detect if a contract is // under construction or not. uint256 cs; //solium-disable-next-line assembly { cs := extcodesize(address) } return cs == 0; } // Reserved storage space to allow for layout changes in the future. uint256[50] private ______gap; } /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be aplied to your functions to restrict their use to * the owner. */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } /** * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * > Note: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } /** * @title Proxy * @dev Implements delegation of calls to other contracts, with proper * forwarding of return values and bubbling of failures. * It defines a fallback function that delegates all calls to the address * returned by the abstract _implementation() internal function. */ contract Proxy { /** * @dev Fallback function. * Implemented entirely in `_fallback`. */ function() external payable { _fallback(); } /** * @return The Address of the implementation. */ function _implementation() internal view returns (address); /** * @dev Delegates execution to an implementation contract. * This is a low level function that doesn't return to its internal call site. * It will return to the external caller whatever the implementation returns. * @param implementation Address to delegate. */ function _delegate(address implementation) internal { //solium-disable-next-line assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize) } default { return(0, returndatasize) } } } /** * @dev Function that is run as the first thing in the fallback function. * Can be redefined in derived contracts to add functionality. * Redefinitions must call super._willFallback(). */ function _willFallback() internal {} /** * @dev fallback implementation. * Extracted to enable manual triggering. */ function _fallback() internal { _willFallback(); _delegate(_implementation()); } } /** * @title BaseUpgradeabilityProxy * @dev This contract implements a proxy that allows to change the * implementation address to which it will delegate. * Such a change is called an implementation upgrade. */ contract BaseUpgradeabilityProxy is Proxy { /** * @dev Emitted when the implementation is upgraded. * @param implementation Address of the new implementation. */ event Upgraded(address indexed implementation); /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Returns the current implementation. * @return Address of the current implementation */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; //solium-disable-next-line assembly { impl := sload(slot) } } /** * @dev Upgrades the proxy to a new implementation. * @param newImplementation Address of the new implementation. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Sets the implementation address of the proxy. * @param newImplementation Address of the new implementation. */ function _setImplementation(address newImplementation) internal { require( Address.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address" ); bytes32 slot = IMPLEMENTATION_SLOT; //solium-disable-next-line assembly { sstore(slot, newImplementation) } } } /** * @title BaseAdminUpgradeabilityProxy * @dev This contract combines an upgradeability proxy with an authorization * mechanism for administrative tasks. * All external functions in this contract must be guarded by the * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity * feature proposal that would enable this to be done automatically. */ contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy { /** * @dev Emitted when the administration has been transferred. * @param previousAdmin Address of the previous admin. * @param newAdmin Address of the new admin. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Modifier to check whether the `msg.sender` is the admin. * If it is, it will run the function. Otherwise, it will delegate the call * to the implementation. */ modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } /** * @return The address of the proxy admin. */ function admin() external ifAdmin returns (address) { return _admin(); } /** * @return The address of the implementation. */ function implementation() external ifAdmin returns (address) { return _implementation(); } /** * @dev Changes the admin of the proxy. * Only the current admin can call this function. * @param newAdmin Address to transfer proxy administration to. */ function changeAdmin(address newAdmin) external ifAdmin { require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); emit AdminChanged(_admin(), newAdmin); _setAdmin(newAdmin); } /** * @dev Upgrade the backing implementation of the proxy. * Only the admin can call this function. * @param newImplementation Address of the new implementation. */ function upgradeTo(address newImplementation) external ifAdmin { _upgradeTo(newImplementation); } /** * @dev Upgrade the backing implementation of the proxy and call a function * on the new implementation. * This is useful to initialize the proxied contract. * @param newImplementation Address of the new implementation. * @param data Data to send as msg.data in the low level call. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. */ function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { _upgradeTo(newImplementation); (bool success, ) = newImplementation.delegatecall(data); require(success); } /** * @return The admin slot. */ function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; //solium-disable-next-line assembly { adm := sload(slot) } } /** * @dev Sets the address of the proxy admin. * @param newAdmin Address of the new proxy admin. */ function _setAdmin(address newAdmin) internal { bytes32 slot = ADMIN_SLOT; //solium-disable-next-line assembly { sstore(slot, newAdmin) } } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal { require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); super._willFallback(); } } /** * @title UpgradeabilityProxy * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing * implementation and init data. */ contract UpgradeabilityProxy is BaseUpgradeabilityProxy { /** * @dev Contract constructor. * @param _logic Address of the initial implementation. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ constructor(address _logic, bytes memory _data) public payable { assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); _setImplementation(_logic); if (_data.length > 0) { (bool success, ) = _logic.delegatecall(_data); require(success); } } } /** * @title AdminUpgradeabilityProxy * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for * initializing the implementation, admin, and init data. */ contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy { /** * Contract constructor. * @param _logic address of the initial implementation. * @param _admin Address of the proxy administrator. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ constructor(address _logic, address _admin, bytes memory _data) public payable UpgradeabilityProxy(_logic, _data) { assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); _setAdmin(_admin); } } /** * @title InitializableUpgradeabilityProxy * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing * implementation and init data. */ contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy { /** * @dev Contract initializer. * @param _logic Address of the initial implementation. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ function initialize(address _logic, bytes memory _data) public payable { require(_implementation() == address(0)); assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); _setImplementation(_logic); if (_data.length > 0) { (bool success, ) = _logic.delegatecall(_data); require(success); } } } contract AddressStorage { mapping(bytes32 => address) private addresses; function getAddress(bytes32 _key) public view returns (address) { return addresses[_key]; } function _setAddress(bytes32 _key, address _value) internal { addresses[_key] = _value; } } /** * @title InitializableAdminUpgradeabilityProxy * @dev Extends from BaseAdminUpgradeabilityProxy with an initializer for * initializing the implementation, admin, and init data. */ contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy { /** * Contract initializer. * @param _logic address of the initial implementation. * @param _admin Address of the proxy administrator. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ function initialize(address _logic, address _admin, bytes memory _data) public payable { require(_implementation() == address(0)); InitializableUpgradeabilityProxy.initialize(_logic, _data); assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); _setAdmin(_admin); } } /** @title ILendingPoolAddressesProvider interface @notice provides the interface to fetch the LendingPoolCore address */ contract ILendingPoolAddressesProvider { function getLendingPool() public view returns (address); function setLendingPoolImpl(address _pool) public; function getLendingPoolCore() public view returns (address payable); function setLendingPoolCoreImpl(address _lendingPoolCore) public; function getLendingPoolConfigurator() public view returns (address); function setLendingPoolConfiguratorImpl(address _configurator) public; function getLendingPoolDataProvider() public view returns (address); function setLendingPoolDataProviderImpl(address _provider) public; function getLendingPoolParametersProvider() public view returns (address); function setLendingPoolParametersProviderImpl(address _parametersProvider) public; function getTokenDistributor() public view returns (address); function setTokenDistributor(address _tokenDistributor) public; function getFeeProvider() public view returns (address); function setFeeProviderImpl(address _feeProvider) public; function getLendingPoolLiquidationManager() public view returns (address); function setLendingPoolLiquidationManager(address _manager) public; function getLendingPoolManager() public view returns (address); function setLendingPoolManager(address _lendingPoolManager) public; function getPriceOracle() public view returns (address); function setPriceOracle(address _priceOracle) public; function getLendingRateOracle() public view returns (address); function setLendingRateOracle(address _lendingRateOracle) public; } /** * @title LendingPoolAddressesProvider contract * @notice Is the main registry of the protocol. All the different components of the protocol are accessible * through the addresses provider. * @author Aave **/ contract LendingPoolAddressesProvider is Ownable, ILendingPoolAddressesProvider, AddressStorage { //events event LendingPoolUpdated(address indexed newAddress); event LendingPoolCoreUpdated(address indexed newAddress); event LendingPoolParametersProviderUpdated(address indexed newAddress); event LendingPoolManagerUpdated(address indexed newAddress); event LendingPoolConfiguratorUpdated(address indexed newAddress); event LendingPoolLiquidationManagerUpdated(address indexed newAddress); event LendingPoolDataProviderUpdated(address indexed newAddress); event EthereumAddressUpdated(address indexed newAddress); event PriceOracleUpdated(address indexed newAddress); event LendingRateOracleUpdated(address indexed newAddress); event FeeProviderUpdated(address indexed newAddress); event TokenDistributorUpdated(address indexed newAddress); event ProxyCreated(bytes32 id, address indexed newAddress); bytes32 private constant LENDING_POOL = "LENDING_POOL"; bytes32 private constant LENDING_POOL_CORE = "LENDING_POOL_CORE"; bytes32 private constant LENDING_POOL_CONFIGURATOR = "LENDING_POOL_CONFIGURATOR"; bytes32 private constant LENDING_POOL_PARAMETERS_PROVIDER = "PARAMETERS_PROVIDER"; bytes32 private constant LENDING_POOL_MANAGER = "LENDING_POOL_MANAGER"; bytes32 private constant LENDING_POOL_LIQUIDATION_MANAGER = "LIQUIDATION_MANAGER"; bytes32 private constant LENDING_POOL_FLASHLOAN_PROVIDER = "FLASHLOAN_PROVIDER"; bytes32 private constant DATA_PROVIDER = "DATA_PROVIDER"; bytes32 private constant ETHEREUM_ADDRESS = "ETHEREUM_ADDRESS"; bytes32 private constant PRICE_ORACLE = "PRICE_ORACLE"; bytes32 private constant LENDING_RATE_ORACLE = "LENDING_RATE_ORACLE"; bytes32 private constant FEE_PROVIDER = "FEE_PROVIDER"; bytes32 private constant WALLET_BALANCE_PROVIDER = "WALLET_BALANCE_PROVIDER"; bytes32 private constant TOKEN_DISTRIBUTOR = "TOKEN_DISTRIBUTOR"; /** * @dev returns the address of the LendingPool proxy * @return the lending pool proxy address **/ function getLendingPool() public view returns (address) { return getAddress(LENDING_POOL); } /** * @dev updates the implementation of the lending pool * @param _pool the new lending pool implementation **/ function setLendingPoolImpl(address _pool) public onlyOwner { updateImplInternal(LENDING_POOL, _pool); emit LendingPoolUpdated(_pool); } /** * @dev returns the address of the LendingPoolCore proxy * @return the lending pool core proxy address */ function getLendingPoolCore() public view returns (address payable) { address payable core = address(uint160(getAddress(LENDING_POOL_CORE))); return core; } /** * @dev updates the implementation of the lending pool core * @param _lendingPoolCore the new lending pool core implementation **/ function setLendingPoolCoreImpl(address _lendingPoolCore) public onlyOwner { updateImplInternal(LENDING_POOL_CORE, _lendingPoolCore); emit LendingPoolCoreUpdated(_lendingPoolCore); } /** * @dev returns the address of the LendingPoolConfigurator proxy * @return the lending pool configurator proxy address **/ function getLendingPoolConfigurator() public view returns (address) { return getAddress(LENDING_POOL_CONFIGURATOR); } /** * @dev updates the implementation of the lending pool configurator * @param _configurator the new lending pool configurator implementation **/ function setLendingPoolConfiguratorImpl(address _configurator) public onlyOwner { updateImplInternal(LENDING_POOL_CONFIGURATOR, _configurator); emit LendingPoolConfiguratorUpdated(_configurator); } /** * @dev returns the address of the LendingPoolDataProvider proxy * @return the lending pool data provider proxy address */ function getLendingPoolDataProvider() public view returns (address) { return getAddress(DATA_PROVIDER); } /** * @dev updates the implementation of the lending pool data provider * @param _provider the new lending pool data provider implementation **/ function setLendingPoolDataProviderImpl(address _provider) public onlyOwner { updateImplInternal(DATA_PROVIDER, _provider); emit LendingPoolDataProviderUpdated(_provider); } /** * @dev returns the address of the LendingPoolParametersProvider proxy * @return the address of the Lending pool parameters provider proxy **/ function getLendingPoolParametersProvider() public view returns (address) { return getAddress(LENDING_POOL_PARAMETERS_PROVIDER); } /** * @dev updates the implementation of the lending pool parameters provider * @param _parametersProvider the new lending pool parameters provider implementation **/ function setLendingPoolParametersProviderImpl(address _parametersProvider) public onlyOwner { updateImplInternal(LENDING_POOL_PARAMETERS_PROVIDER, _parametersProvider); emit LendingPoolParametersProviderUpdated(_parametersProvider); } /** * @dev returns the address of the FeeProvider proxy * @return the address of the Fee provider proxy **/ function getFeeProvider() public view returns (address) { return getAddress(FEE_PROVIDER); } /** * @dev updates the implementation of the FeeProvider proxy * @param _feeProvider the new lending pool fee provider implementation **/ function setFeeProviderImpl(address _feeProvider) public onlyOwner { updateImplInternal(FEE_PROVIDER, _feeProvider); emit FeeProviderUpdated(_feeProvider); } /** * @dev returns the address of the LendingPoolLiquidationManager. Since the manager is used * through delegateCall within the LendingPool contract, the proxy contract pattern does not work properly hence * the addresses are changed directly. * @return the address of the Lending pool liquidation manager **/ function getLendingPoolLiquidationManager() public view returns (address) { return getAddress(LENDING_POOL_LIQUIDATION_MANAGER); } /** * @dev updates the address of the Lending pool liquidation manager * @param _manager the new lending pool liquidation manager address **/ function setLendingPoolLiquidationManager(address _manager) public onlyOwner { _setAddress(LENDING_POOL_LIQUIDATION_MANAGER, _manager); emit LendingPoolLiquidationManagerUpdated(_manager); } /** * @dev the functions below are storing specific addresses that are outside the context of the protocol * hence the upgradable proxy pattern is not used **/ function getLendingPoolManager() public view returns (address) { return getAddress(LENDING_POOL_MANAGER); } function setLendingPoolManager(address _lendingPoolManager) public onlyOwner { _setAddress(LENDING_POOL_MANAGER, _lendingPoolManager); emit LendingPoolManagerUpdated(_lendingPoolManager); } function getPriceOracle() public view returns (address) { return getAddress(PRICE_ORACLE); } function setPriceOracle(address _priceOracle) public onlyOwner { _setAddress(PRICE_ORACLE, _priceOracle); emit PriceOracleUpdated(_priceOracle); } function getLendingRateOracle() public view returns (address) { return getAddress(LENDING_RATE_ORACLE); } function setLendingRateOracle(address _lendingRateOracle) public onlyOwner { _setAddress(LENDING_RATE_ORACLE, _lendingRateOracle); emit LendingRateOracleUpdated(_lendingRateOracle); } function getTokenDistributor() public view returns (address) { return getAddress(TOKEN_DISTRIBUTOR); } function setTokenDistributor(address _tokenDistributor) public onlyOwner { _setAddress(TOKEN_DISTRIBUTOR, _tokenDistributor); emit TokenDistributorUpdated(_tokenDistributor); } /** * @dev internal function to update the implementation of a specific component of the protocol * @param _id the id of the contract to be updated * @param _newAddress the address of the new implementation **/ function updateImplInternal(bytes32 _id, address _newAddress) internal { address payable proxyAddress = address(uint160(getAddress(_id))); InitializableAdminUpgradeabilityProxy proxy = InitializableAdminUpgradeabilityProxy(proxyAddress); bytes memory params = abi.encodeWithSignature("initialize(address)", address(this)); if (proxyAddress == address(0)) { proxy = new InitializableAdminUpgradeabilityProxy(); proxy.initialize(_newAddress, address(this), params); _setAddress(_id, address(proxy)); emit ProxyCreated(_id, address(proxy)); } else { proxy.upgradeToAndCall(_newAddress, params); } } } contract UintStorage { mapping(bytes32 => uint256) private uints; function getUint(bytes32 _key) public view returns (uint256) { return uints[_key]; } function _setUint(bytes32 _key, uint256 _value) internal { uints[_key] = _value; } } /** * @title LendingPoolParametersProvider * @author Aave * @notice stores the configuration parameters of the Lending Pool contract **/ contract LendingPoolParametersProvider is VersionedInitializable { uint256 private constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 25; uint256 private constant REBALANCE_DOWN_RATE_DELTA = (1e27)/5; uint256 private constant FLASHLOAN_FEE_TOTAL = 35; uint256 private constant FLASHLOAN_FEE_PROTOCOL = 3000; uint256 constant private DATA_PROVIDER_REVISION = 0x1; function getRevision() internal pure returns(uint256) { return DATA_PROVIDER_REVISION; } /** * @dev initializes the LendingPoolParametersProvider after it's added to the proxy * @param _addressesProvider the address of the LendingPoolAddressesProvider */ function initialize(address _addressesProvider) public initializer { } /** * @dev returns the maximum stable rate borrow size, in percentage of the available liquidity. **/ function getMaxStableRateBorrowSizePercent() external pure returns (uint256) { return MAX_STABLE_RATE_BORROW_SIZE_PERCENT; } /** * @dev returns the delta between the current stable rate and the user stable rate at * which the borrow position of the user will be rebalanced (scaled down) **/ function getRebalanceDownRateDelta() external pure returns (uint256) { return REBALANCE_DOWN_RATE_DELTA; } /** * @dev returns the fee applied to a flashloan and the portion to redirect to the protocol, in basis points. **/ function getFlashLoanFeesInBips() external pure returns (uint256, uint256) { return (FLASHLOAN_FEE_TOTAL, FLASHLOAN_FEE_PROTOCOL); } } /** * @title CoreLibrary library * @author Aave * @notice Defines the data structures of the reserves and the user data **/ library CoreLibrary { using SafeMath for uint256; using WadRayMath for uint256; enum InterestRateMode {NONE, STABLE, VARIABLE} uint256 internal constant SECONDS_PER_YEAR = 365 days; struct UserReserveData { //principal amount borrowed by the user. uint256 principalBorrowBalance; //cumulated variable borrow index for the user. Expressed in ray uint256 lastVariableBorrowCumulativeIndex; //origination fee cumulated by the user uint256 originationFee; // stable borrow rate at which the user has borrowed. Expressed in ray uint256 stableBorrowRate; uint40 lastUpdateTimestamp; //defines if a specific deposit should or not be used as a collateral in borrows bool useAsCollateral; } struct ReserveData { /** * @dev refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties. **/ //the liquidity index. Expressed in ray uint256 lastLiquidityCumulativeIndex; //the current supply rate. Expressed in ray uint256 currentLiquidityRate; //the total borrows of the reserve at a stable rate. Expressed in the currency decimals uint256 totalBorrowsStable; //the total borrows of the reserve at a variable rate. Expressed in the currency decimals uint256 totalBorrowsVariable; //the current variable borrow rate. Expressed in ray uint256 currentVariableBorrowRate; //the current stable borrow rate. Expressed in ray uint256 currentStableBorrowRate; //the current average stable borrow rate (weighted average of all the different stable rate loans). Expressed in ray uint256 currentAverageStableBorrowRate; //variable borrow index. Expressed in ray uint256 lastVariableBorrowCumulativeIndex; //the ltv of the reserve. Expressed in percentage (0-100) uint256 baseLTVasCollateral; //the liquidation threshold of the reserve. Expressed in percentage (0-100) uint256 liquidationThreshold; //the liquidation bonus of the reserve. Expressed in percentage uint256 liquidationBonus; //the decimals of the reserve asset uint256 decimals; /** * @dev address of the aToken representing the asset **/ address aTokenAddress; /** * @dev address of the interest rate strategy contract **/ address interestRateStrategyAddress; uint40 lastUpdateTimestamp; // borrowingEnabled = true means users can borrow from this reserve bool borrowingEnabled; // usageAsCollateralEnabled = true means users can use this reserve as collateral bool usageAsCollateralEnabled; // isStableBorrowRateEnabled = true means users can borrow at a stable rate bool isStableBorrowRateEnabled; // isActive = true means the reserve has been activated and properly configured bool isActive; // isFreezed = true means the reserve only allows repays and redeems, but not deposits, new borrowings or rate swap bool isFreezed; } /** * @dev returns the ongoing normalized income for the reserve. * a value of 1e27 means there is no income. As time passes, the income is accrued. * A value of 2*1e27 means that the income of the reserve is double the initial amount. * @param _reserve the reserve object * @return the normalized income. expressed in ray **/ function getNormalizedIncome(CoreLibrary.ReserveData storage _reserve) internal view returns (uint256) { uint256 cumulated = calculateLinearInterest( _reserve .currentLiquidityRate, _reserve .lastUpdateTimestamp ) .rayMul(_reserve.lastLiquidityCumulativeIndex); return cumulated; } /** * @dev Updates the liquidity cumulative index Ci and variable borrow cumulative index Bvc. Refer to the whitepaper for * a formal specification. * @param _self the reserve object **/ function updateCumulativeIndexes(ReserveData storage _self) internal { uint256 totalBorrows = getTotalBorrows(_self); if (totalBorrows > 0) { //only cumulating if there is any income being produced uint256 cumulatedLiquidityInterest = calculateLinearInterest( _self.currentLiquidityRate, _self.lastUpdateTimestamp ); _self.lastLiquidityCumulativeIndex = cumulatedLiquidityInterest.rayMul( _self.lastLiquidityCumulativeIndex ); uint256 cumulatedVariableBorrowInterest = calculateCompoundedInterest( _self.currentVariableBorrowRate, _self.lastUpdateTimestamp ); _self.lastVariableBorrowCumulativeIndex = cumulatedVariableBorrowInterest.rayMul( _self.lastVariableBorrowCumulativeIndex ); } } /** * @dev accumulates a predefined amount of asset to the reserve as a fixed, one time income. Used for example to accumulate * the flashloan fee to the reserve, and spread it through the depositors. * @param _self the reserve object * @param _totalLiquidity the total liquidity available in the reserve * @param _amount the amount to accomulate **/ function cumulateToLiquidityIndex( ReserveData storage _self, uint256 _totalLiquidity, uint256 _amount ) internal { uint256 amountToLiquidityRatio = _amount.wadToRay().rayDiv(_totalLiquidity.wadToRay()); uint256 cumulatedLiquidity = amountToLiquidityRatio.add(WadRayMath.ray()); _self.lastLiquidityCumulativeIndex = cumulatedLiquidity.rayMul( _self.lastLiquidityCumulativeIndex ); } /** * @dev initializes a reserve * @param _self the reserve object * @param _aTokenAddress the address of the overlying atoken contract * @param _decimals the number of decimals of the underlying asset * @param _interestRateStrategyAddress the address of the interest rate strategy contract **/ function init( ReserveData storage _self, address _aTokenAddress, uint256 _decimals, address _interestRateStrategyAddress ) external { require(_self.aTokenAddress == address(0), "Reserve has already been initialized"); if (_self.lastLiquidityCumulativeIndex == 0) { //if the reserve has not been initialized yet _self.lastLiquidityCumulativeIndex = WadRayMath.ray(); } if (_self.lastVariableBorrowCumulativeIndex == 0) { _self.lastVariableBorrowCumulativeIndex = WadRayMath.ray(); } _self.aTokenAddress = _aTokenAddress; _self.decimals = _decimals; _self.interestRateStrategyAddress = _interestRateStrategyAddress; _self.isActive = true; _self.isFreezed = false; } /** * @dev enables borrowing on a reserve * @param _self the reserve object * @param _stableBorrowRateEnabled true if the stable borrow rate must be enabled by default, false otherwise **/ function enableBorrowing(ReserveData storage _self, bool _stableBorrowRateEnabled) external { require(_self.borrowingEnabled == false, "Reserve is already enabled"); _self.borrowingEnabled = true; _self.isStableBorrowRateEnabled = _stableBorrowRateEnabled; } /** * @dev disables borrowing on a reserve * @param _self the reserve object **/ function disableBorrowing(ReserveData storage _self) external { _self.borrowingEnabled = false; } /** * @dev enables a reserve to be used as collateral * @param _self the reserve object * @param _baseLTVasCollateral the loan to value of the asset when used as collateral * @param _liquidationThreshold the threshold at which loans using this asset as collateral will be considered undercollateralized * @param _liquidationBonus the bonus liquidators receive to liquidate this asset **/ function enableAsCollateral( ReserveData storage _self, uint256 _baseLTVasCollateral, uint256 _liquidationThreshold, uint256 _liquidationBonus ) external { require( _self.usageAsCollateralEnabled == false, "Reserve is already enabled as collateral" ); _self.usageAsCollateralEnabled = true; _self.baseLTVasCollateral = _baseLTVasCollateral; _self.liquidationThreshold = _liquidationThreshold; _self.liquidationBonus = _liquidationBonus; if (_self.lastLiquidityCumulativeIndex == 0) _self.lastLiquidityCumulativeIndex = WadRayMath.ray(); } /** * @dev disables a reserve as collateral * @param _self the reserve object **/ function disableAsCollateral(ReserveData storage _self) external { _self.usageAsCollateralEnabled = false; } /** * @dev calculates the compounded borrow balance of a user * @param _self the userReserve object * @param _reserve the reserve object * @return the user compounded borrow balance **/ function getCompoundedBorrowBalance( CoreLibrary.UserReserveData storage _self, CoreLibrary.ReserveData storage _reserve ) internal view returns (uint256) { if (_self.principalBorrowBalance == 0) return 0; uint256 principalBorrowBalanceRay = _self.principalBorrowBalance.wadToRay(); uint256 compoundedBalance = 0; uint256 cumulatedInterest = 0; if (_self.stableBorrowRate > 0) { cumulatedInterest = calculateCompoundedInterest( _self.stableBorrowRate, _self.lastUpdateTimestamp ); } else { //variable interest cumulatedInterest = calculateCompoundedInterest( _reserve .currentVariableBorrowRate, _reserve .lastUpdateTimestamp ) .rayMul(_reserve.lastVariableBorrowCumulativeIndex) .rayDiv(_self.lastVariableBorrowCumulativeIndex); } compoundedBalance = principalBorrowBalanceRay.rayMul(cumulatedInterest).rayToWad(); if (compoundedBalance == _self.principalBorrowBalance) { //solium-disable-next-line if (_self.lastUpdateTimestamp != block.timestamp) { //no interest cumulation because of the rounding - we add 1 wei //as symbolic cumulated interest to avoid interest free loans. return _self.principalBorrowBalance.add(1 wei); } } return compoundedBalance; } /** * @dev increases the total borrows at a stable rate on a specific reserve and updates the * average stable rate consequently * @param _reserve the reserve object * @param _amount the amount to add to the total borrows stable * @param _rate the rate at which the amount has been borrowed **/ function increaseTotalBorrowsStableAndUpdateAverageRate( ReserveData storage _reserve, uint256 _amount, uint256 _rate ) internal { uint256 previousTotalBorrowStable = _reserve.totalBorrowsStable; //updating reserve borrows stable _reserve.totalBorrowsStable = _reserve.totalBorrowsStable.add(_amount); //update the average stable rate //weighted average of all the borrows uint256 weightedLastBorrow = _amount.wadToRay().rayMul(_rate); uint256 weightedPreviousTotalBorrows = previousTotalBorrowStable.wadToRay().rayMul( _reserve.currentAverageStableBorrowRate ); _reserve.currentAverageStableBorrowRate = weightedLastBorrow .add(weightedPreviousTotalBorrows) .rayDiv(_reserve.totalBorrowsStable.wadToRay()); } /** * @dev decreases the total borrows at a stable rate on a specific reserve and updates the * average stable rate consequently * @param _reserve the reserve object * @param _amount the amount to substract to the total borrows stable * @param _rate the rate at which the amount has been repaid **/ function decreaseTotalBorrowsStableAndUpdateAverageRate( ReserveData storage _reserve, uint256 _amount, uint256 _rate ) internal { require(_reserve.totalBorrowsStable >= _amount, "Invalid amount to decrease"); uint256 previousTotalBorrowStable = _reserve.totalBorrowsStable; //updating reserve borrows stable _reserve.totalBorrowsStable = _reserve.totalBorrowsStable.sub(_amount); if (_reserve.totalBorrowsStable == 0) { _reserve.currentAverageStableBorrowRate = 0; //no income if there are no stable rate borrows return; } //update the average stable rate //weighted average of all the borrows uint256 weightedLastBorrow = _amount.wadToRay().rayMul(_rate); uint256 weightedPreviousTotalBorrows = previousTotalBorrowStable.wadToRay().rayMul( _reserve.currentAverageStableBorrowRate ); require( weightedPreviousTotalBorrows >= weightedLastBorrow, "The amounts to subtract don't match" ); _reserve.currentAverageStableBorrowRate = weightedPreviousTotalBorrows .sub(weightedLastBorrow) .rayDiv(_reserve.totalBorrowsStable.wadToRay()); } /** * @dev increases the total borrows at a variable rate * @param _reserve the reserve object * @param _amount the amount to add to the total borrows variable **/ function increaseTotalBorrowsVariable(ReserveData storage _reserve, uint256 _amount) internal { _reserve.totalBorrowsVariable = _reserve.totalBorrowsVariable.add(_amount); } /** * @dev decreases the total borrows at a variable rate * @param _reserve the reserve object * @param _amount the amount to substract to the total borrows variable **/ function decreaseTotalBorrowsVariable(ReserveData storage _reserve, uint256 _amount) internal { require( _reserve.totalBorrowsVariable >= _amount, "The amount that is being subtracted from the variable total borrows is incorrect" ); _reserve.totalBorrowsVariable = _reserve.totalBorrowsVariable.sub(_amount); } /** * @dev function to calculate the interest using a linear interest rate formula * @param _rate the interest rate, in ray * @param _lastUpdateTimestamp the timestamp of the last update of the interest * @return the interest rate linearly accumulated during the timeDelta, in ray **/ function calculateLinearInterest(uint256 _rate, uint40 _lastUpdateTimestamp) internal view returns (uint256) { //solium-disable-next-line uint256 timeDifference = block.timestamp.sub(uint256(_lastUpdateTimestamp)); uint256 timeDelta = timeDifference.wadToRay().rayDiv(SECONDS_PER_YEAR.wadToRay()); return _rate.rayMul(timeDelta).add(WadRayMath.ray()); } /** * @dev function to calculate the interest using a compounded interest rate formula * @param _rate the interest rate, in ray * @param _lastUpdateTimestamp the timestamp of the last update of the interest * @return the interest rate compounded during the timeDelta, in ray **/ function calculateCompoundedInterest(uint256 _rate, uint40 _lastUpdateTimestamp) internal view returns (uint256) { //solium-disable-next-line uint256 timeDifference = block.timestamp.sub(uint256(_lastUpdateTimestamp)); uint256 ratePerSecond = _rate.div(SECONDS_PER_YEAR); return ratePerSecond.add(WadRayMath.ray()).rayPow(timeDifference); } /** * @dev returns the total borrows on the reserve * @param _reserve the reserve object * @return the total borrows (stable + variable) **/ function getTotalBorrows(CoreLibrary.ReserveData storage _reserve) internal view returns (uint256) { return _reserve.totalBorrowsStable.add(_reserve.totalBorrowsVariable); } } /** * @title IPriceOracleGetter interface * @notice Interface for the Aave price oracle. **/ interface IPriceOracleGetter { /** * @dev returns the asset price in ETH * @param _asset the address of the asset * @return the ETH price of the asset **/ function getAssetPrice(address _asset) external view returns (uint256); } /** * @title IFeeProvider interface * @notice Interface for the Aave fee provider. **/ interface IFeeProvider { function calculateLoanOriginationFee(address _user, uint256 _amount) external view returns (uint256); function getLoanOriginationFeePercentage() external view returns (uint256); } /** * @title LendingPoolDataProvider contract * @author Aave * @notice Implements functions to fetch data from the core, and aggregate them in order to allow computation * on the compounded balances and the account balances in ETH **/ contract LendingPoolDataProvider is VersionedInitializable { using SafeMath for uint256; using WadRayMath for uint256; LendingPoolCore public core; LendingPoolAddressesProvider public addressesProvider; /** * @dev specifies the health factor threshold at which the user position is liquidated. * 1e18 by default, if the health factor drops below 1e18, the loan can be liquidated. **/ uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18; uint256 public constant DATA_PROVIDER_REVISION = 0x1; function getRevision() internal pure returns (uint256) { return DATA_PROVIDER_REVISION; } function initialize(LendingPoolAddressesProvider _addressesProvider) public initializer { addressesProvider = _addressesProvider; core = LendingPoolCore(_addressesProvider.getLendingPoolCore()); } /** * @dev struct to hold calculateUserGlobalData() local computations **/ struct UserGlobalDataLocalVars { uint256 reserveUnitPrice; uint256 tokenUnit; uint256 compoundedLiquidityBalance; uint256 compoundedBorrowBalance; uint256 reserveDecimals; uint256 baseLtv; uint256 liquidationThreshold; uint256 originationFee; bool usageAsCollateralEnabled; bool userUsesReserveAsCollateral; address currentReserve; } /** * @dev calculates the user data across the reserves. * this includes the total liquidity/collateral/borrow balances in ETH, * the average Loan To Value, the average Liquidation Ratio, and the Health factor. * @param _user the address of the user * @return the total liquidity, total collateral, total borrow balances of the user in ETH. * also the average Ltv, liquidation threshold, and the health factor **/ function calculateUserGlobalData(address _user) public view returns ( uint256 totalLiquidityBalanceETH, uint256 totalCollateralBalanceETH, uint256 totalBorrowBalanceETH, uint256 totalFeesETH, uint256 currentLtv, uint256 currentLiquidationThreshold, uint256 healthFactor, bool healthFactorBelowThreshold ) { IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle()); // Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables UserGlobalDataLocalVars memory vars; address[] memory reserves = core.getReserves(); for (uint256 i = 0; i < reserves.length; i++) { vars.currentReserve = reserves[i]; ( vars.compoundedLiquidityBalance, vars.compoundedBorrowBalance, vars.originationFee, vars.userUsesReserveAsCollateral ) = core.getUserBasicReserveData(vars.currentReserve, _user); if (vars.compoundedLiquidityBalance == 0 && vars.compoundedBorrowBalance == 0) { continue; } //fetch reserve data ( vars.reserveDecimals, vars.baseLtv, vars.liquidationThreshold, vars.usageAsCollateralEnabled ) = core.getReserveConfiguration(vars.currentReserve); vars.tokenUnit = 10 ** vars.reserveDecimals; vars.reserveUnitPrice = oracle.getAssetPrice(vars.currentReserve); //liquidity and collateral balance if (vars.compoundedLiquidityBalance > 0) { uint256 liquidityBalanceETH = vars .reserveUnitPrice .mul(vars.compoundedLiquidityBalance) .div(vars.tokenUnit); totalLiquidityBalanceETH = totalLiquidityBalanceETH.add(liquidityBalanceETH); if (vars.usageAsCollateralEnabled && vars.userUsesReserveAsCollateral) { totalCollateralBalanceETH = totalCollateralBalanceETH.add(liquidityBalanceETH); currentLtv = currentLtv.add(liquidityBalanceETH.mul(vars.baseLtv)); currentLiquidationThreshold = currentLiquidationThreshold.add( liquidityBalanceETH.mul(vars.liquidationThreshold) ); } } if (vars.compoundedBorrowBalance > 0) { totalBorrowBalanceETH = totalBorrowBalanceETH.add( vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit) ); totalFeesETH = totalFeesETH.add( vars.originationFee.mul(vars.reserveUnitPrice).div(vars.tokenUnit) ); } } currentLtv = totalCollateralBalanceETH > 0 ? currentLtv.div(totalCollateralBalanceETH) : 0; currentLiquidationThreshold = totalCollateralBalanceETH > 0 ? currentLiquidationThreshold.div(totalCollateralBalanceETH) : 0; healthFactor = calculateHealthFactorFromBalancesInternal( totalCollateralBalanceETH, totalBorrowBalanceETH, totalFeesETH, currentLiquidationThreshold ); healthFactorBelowThreshold = healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD; } struct balanceDecreaseAllowedLocalVars { uint256 decimals; uint256 collateralBalanceETH; uint256 borrowBalanceETH; uint256 totalFeesETH; uint256 currentLiquidationThreshold; uint256 reserveLiquidationThreshold; uint256 amountToDecreaseETH; uint256 collateralBalancefterDecrease; uint256 liquidationThresholdAfterDecrease; uint256 healthFactorAfterDecrease; bool reserveUsageAsCollateralEnabled; } /** * @dev check if a specific balance decrease is allowed (i.e. doesn't bring the user borrow position health factor under 1e18) * @param _reserve the address of the reserve * @param _user the address of the user * @param _amount the amount to decrease * @return true if the decrease of the balance is allowed **/ function balanceDecreaseAllowed(address _reserve, address _user, uint256 _amount) external view returns (bool) { // Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables balanceDecreaseAllowedLocalVars memory vars; ( vars.decimals, , vars.reserveLiquidationThreshold, vars.reserveUsageAsCollateralEnabled ) = core.getReserveConfiguration(_reserve); if ( !vars.reserveUsageAsCollateralEnabled || !core.isUserUseReserveAsCollateralEnabled(_reserve, _user) ) { return true; //if reserve is not used as collateral, no reasons to block the transfer } ( , vars.collateralBalanceETH, vars.borrowBalanceETH, vars.totalFeesETH, , vars.currentLiquidationThreshold, , ) = calculateUserGlobalData(_user); if (vars.borrowBalanceETH == 0) { return true; //no borrows - no reasons to block the transfer } IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle()); vars.amountToDecreaseETH = oracle.getAssetPrice(_reserve).mul(_amount).div( 10 ** vars.decimals ); vars.collateralBalancefterDecrease = vars.collateralBalanceETH.sub( vars.amountToDecreaseETH ); //if there is a borrow, there can't be 0 collateral if (vars.collateralBalancefterDecrease == 0) { return false; } vars.liquidationThresholdAfterDecrease = vars .collateralBalanceETH .mul(vars.currentLiquidationThreshold) .sub(vars.amountToDecreaseETH.mul(vars.reserveLiquidationThreshold)) .div(vars.collateralBalancefterDecrease); uint256 healthFactorAfterDecrease = calculateHealthFactorFromBalancesInternal( vars.collateralBalancefterDecrease, vars.borrowBalanceETH, vars.totalFeesETH, vars.liquidationThresholdAfterDecrease ); return healthFactorAfterDecrease > HEALTH_FACTOR_LIQUIDATION_THRESHOLD; } /** * @notice calculates the amount of collateral needed in ETH to cover a new borrow. * @param _reserve the reserve from which the user wants to borrow * @param _amount the amount the user wants to borrow * @param _fee the fee for the amount that the user needs to cover * @param _userCurrentBorrowBalanceTH the current borrow balance of the user (before the borrow) * @param _userCurrentLtv the average ltv of the user given his current collateral * @return the total amount of collateral in ETH to cover the current borrow balance + the new amount + fee **/ function calculateCollateralNeededInETH( address _reserve, uint256 _amount, uint256 _fee, uint256 _userCurrentBorrowBalanceTH, uint256 _userCurrentFeesETH, uint256 _userCurrentLtv ) external view returns (uint256) { uint256 reserveDecimals = core.getReserveDecimals(_reserve); IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle()); uint256 requestedBorrowAmountETH = oracle .getAssetPrice(_reserve) .mul(_amount.add(_fee)) .div(10 ** reserveDecimals); //price is in ether //add the current already borrowed amount to the amount requested to calculate the total collateral needed. uint256 collateralNeededInETH = _userCurrentBorrowBalanceTH .add(_userCurrentFeesETH) .add(requestedBorrowAmountETH) .mul(100) .div(_userCurrentLtv); //LTV is calculated in percentage return collateralNeededInETH; } /** * @dev calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the * average Loan To Value. * @param collateralBalanceETH the total collateral balance * @param borrowBalanceETH the total borrow balance * @param totalFeesETH the total fees * @param ltv the average loan to value * @return the amount available to borrow in ETH for the user **/ function calculateAvailableBorrowsETHInternal( uint256 collateralBalanceETH, uint256 borrowBalanceETH, uint256 totalFeesETH, uint256 ltv ) internal view returns (uint256) { uint256 availableBorrowsETH = collateralBalanceETH.mul(ltv).div(100); //ltv is in percentage if (availableBorrowsETH < borrowBalanceETH) { return 0; } availableBorrowsETH = availableBorrowsETH.sub(borrowBalanceETH.add(totalFeesETH)); //calculate fee uint256 borrowFee = IFeeProvider(addressesProvider.getFeeProvider()) .calculateLoanOriginationFee(msg.sender, availableBorrowsETH); return availableBorrowsETH.sub(borrowFee); } /** * @dev calculates the health factor from the corresponding balances * @param collateralBalanceETH the total collateral balance in ETH * @param borrowBalanceETH the total borrow balance in ETH * @param totalFeesETH the total fees in ETH * @param liquidationThreshold the avg liquidation threshold **/ function calculateHealthFactorFromBalancesInternal( uint256 collateralBalanceETH, uint256 borrowBalanceETH, uint256 totalFeesETH, uint256 liquidationThreshold ) internal pure returns (uint256) { if (borrowBalanceETH == 0) return uint256(-1); return (collateralBalanceETH.mul(liquidationThreshold).div(100)).wadDiv( borrowBalanceETH.add(totalFeesETH) ); } /** * @dev returns the health factor liquidation threshold **/ function getHealthFactorLiquidationThreshold() public pure returns (uint256) { return HEALTH_FACTOR_LIQUIDATION_THRESHOLD; } /** * @dev accessory functions to fetch data from the lendingPoolCore **/ function getReserveConfigurationData(address _reserve) external view returns ( uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus, address rateStrategyAddress, bool usageAsCollateralEnabled, bool borrowingEnabled, bool stableBorrowRateEnabled, bool isActive ) { (, ltv, liquidationThreshold, usageAsCollateralEnabled) = core.getReserveConfiguration( _reserve ); stableBorrowRateEnabled = core.getReserveIsStableBorrowRateEnabled(_reserve); borrowingEnabled = core.isReserveBorrowingEnabled(_reserve); isActive = core.getReserveIsActive(_reserve); liquidationBonus = core.getReserveLiquidationBonus(_reserve); rateStrategyAddress = core.getReserveInterestRateStrategyAddress(_reserve); } function getReserveData(address _reserve) external view returns ( uint256 totalLiquidity, uint256 availableLiquidity, uint256 totalBorrowsStable, uint256 totalBorrowsVariable, uint256 liquidityRate, uint256 variableBorrowRate, uint256 stableBorrowRate, uint256 averageStableBorrowRate, uint256 utilizationRate, uint256 liquidityIndex, uint256 variableBorrowIndex, address aTokenAddress, uint40 lastUpdateTimestamp ) { totalLiquidity = core.getReserveTotalLiquidity(_reserve); availableLiquidity = core.getReserveAvailableLiquidity(_reserve); totalBorrowsStable = core.getReserveTotalBorrowsStable(_reserve); totalBorrowsVariable = core.getReserveTotalBorrowsVariable(_reserve); liquidityRate = core.getReserveCurrentLiquidityRate(_reserve); variableBorrowRate = core.getReserveCurrentVariableBorrowRate(_reserve); stableBorrowRate = core.getReserveCurrentStableBorrowRate(_reserve); averageStableBorrowRate = core.getReserveCurrentAverageStableBorrowRate(_reserve); utilizationRate = core.getReserveUtilizationRate(_reserve); liquidityIndex = core.getReserveLiquidityCumulativeIndex(_reserve); variableBorrowIndex = core.getReserveVariableBorrowsCumulativeIndex(_reserve); aTokenAddress = core.getReserveATokenAddress(_reserve); lastUpdateTimestamp = core.getReserveLastUpdate(_reserve); } function getUserAccountData(address _user) external view returns ( uint256 totalLiquidityETH, uint256 totalCollateralETH, uint256 totalBorrowsETH, uint256 totalFeesETH, uint256 availableBorrowsETH, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor ) { ( totalLiquidityETH, totalCollateralETH, totalBorrowsETH, totalFeesETH, ltv, currentLiquidationThreshold, healthFactor, ) = calculateUserGlobalData(_user); availableBorrowsETH = calculateAvailableBorrowsETHInternal( totalCollateralETH, totalBorrowsETH, totalFeesETH, ltv ); } function getUserReserveData(address _reserve, address _user) external view returns ( uint256 currentATokenBalance, uint256 currentBorrowBalance, uint256 principalBorrowBalance, uint256 borrowRateMode, uint256 borrowRate, uint256 liquidityRate, uint256 originationFee, uint256 variableBorrowIndex, uint256 lastUpdateTimestamp, bool usageAsCollateralEnabled ) { currentATokenBalance = AToken(core.getReserveATokenAddress(_reserve)).balanceOf(_user); CoreLibrary.InterestRateMode mode = core.getUserCurrentBorrowRateMode(_reserve, _user); (principalBorrowBalance, currentBorrowBalance, ) = core.getUserBorrowBalances( _reserve, _user ); //default is 0, if mode == CoreLibrary.InterestRateMode.NONE if (mode == CoreLibrary.InterestRateMode.STABLE) { borrowRate = core.getUserCurrentStableBorrowRate(_reserve, _user); } else if (mode == CoreLibrary.InterestRateMode.VARIABLE) { borrowRate = core.getReserveCurrentVariableBorrowRate(_reserve); } borrowRateMode = uint256(mode); liquidityRate = core.getReserveCurrentLiquidityRate(_reserve); originationFee = core.getUserOriginationFee(_reserve, _user); variableBorrowIndex = core.getUserVariableBorrowCumulativeIndex(_reserve, _user); lastUpdateTimestamp = core.getUserLastUpdate(_reserve, _user); usageAsCollateralEnabled = core.isUserUseReserveAsCollateralEnabled(_reserve, _user); } } /** * @title Aave ERC20 AToken * * @dev Implementation of the interest bearing token for the DLP protocol. * @author Aave */ contract AToken is ERC20, ERC20Detailed { using WadRayMath for uint256; uint256 public constant UINT_MAX_VALUE = uint256(-1); /** * @dev emitted after the redeem action * @param _from the address performing the redeem * @param _value the amount to be redeemed * @param _fromBalanceIncrease the cumulated balance since the last update of the user * @param _fromIndex the last index of the user **/ event Redeem( address indexed _from, uint256 _value, uint256 _fromBalanceIncrease, uint256 _fromIndex ); /** * @dev emitted after the mint action * @param _from the address performing the mint * @param _value the amount to be minted * @param _fromBalanceIncrease the cumulated balance since the last update of the user * @param _fromIndex the last index of the user **/ event MintOnDeposit( address indexed _from, uint256 _value, uint256 _fromBalanceIncrease, uint256 _fromIndex ); /** * @dev emitted during the liquidation action, when the liquidator reclaims the underlying * asset * @param _from the address from which the tokens are being burned * @param _value the amount to be burned * @param _fromBalanceIncrease the cumulated balance since the last update of the user * @param _fromIndex the last index of the user **/ event BurnOnLiquidation( address indexed _from, uint256 _value, uint256 _fromBalanceIncrease, uint256 _fromIndex ); /** * @dev emitted during the transfer action * @param _from the address from which the tokens are being transferred * @param _to the adress of the destination * @param _value the amount to be minted * @param _fromBalanceIncrease the cumulated balance since the last update of the user * @param _toBalanceIncrease the cumulated balance since the last update of the destination * @param _fromIndex the last index of the user * @param _toIndex the last index of the liquidator **/ event BalanceTransfer( address indexed _from, address indexed _to, uint256 _value, uint256 _fromBalanceIncrease, uint256 _toBalanceIncrease, uint256 _fromIndex, uint256 _toIndex ); /** * @dev emitted when the accumulation of the interest * by an user is redirected to another user * @param _from the address from which the interest is being redirected * @param _to the adress of the destination * @param _fromBalanceIncrease the cumulated balance since the last update of the user * @param _fromIndex the last index of the user **/ event InterestStreamRedirected( address indexed _from, address indexed _to, uint256 _redirectedBalance, uint256 _fromBalanceIncrease, uint256 _fromIndex ); /** * @dev emitted when the redirected balance of an user is being updated * @param _targetAddress the address of which the balance is being updated * @param _targetBalanceIncrease the cumulated balance since the last update of the target * @param _targetIndex the last index of the user * @param _redirectedBalanceAdded the redirected balance being added * @param _redirectedBalanceRemoved the redirected balance being removed **/ event RedirectedBalanceUpdated( address indexed _targetAddress, uint256 _targetBalanceIncrease, uint256 _targetIndex, uint256 _redirectedBalanceAdded, uint256 _redirectedBalanceRemoved ); event InterestRedirectionAllowanceChanged( address indexed _from, address indexed _to ); address public underlyingAssetAddress; mapping (address => uint256) private userIndexes; mapping (address => address) private interestRedirectionAddresses; mapping (address => uint256) private redirectedBalances; mapping (address => address) private interestRedirectionAllowances; LendingPoolAddressesProvider private addressesProvider; LendingPoolCore private core; LendingPool private pool; LendingPoolDataProvider private dataProvider; modifier onlyLendingPool { require( msg.sender == address(pool), "The caller of this function must be a lending pool" ); _; } modifier whenTransferAllowed(address _from, uint256 _amount) { require(isTransferAllowed(_from, _amount), "Transfer cannot be allowed."); _; } constructor( LendingPoolAddressesProvider _addressesProvider, address _underlyingAsset, uint8 _underlyingAssetDecimals, string memory _name, string memory _symbol ) public ERC20Detailed(_name, _symbol, _underlyingAssetDecimals) { addressesProvider = _addressesProvider; core = LendingPoolCore(addressesProvider.getLendingPoolCore()); pool = LendingPool(addressesProvider.getLendingPool()); dataProvider = LendingPoolDataProvider(addressesProvider.getLendingPoolDataProvider()); underlyingAssetAddress = _underlyingAsset; } /** * @notice ERC20 implementation internal function backing transfer() and transferFrom() * @dev validates the transfer before allowing it. NOTE: This is not standard ERC20 behavior **/ function _transfer(address _from, address _to, uint256 _amount) internal whenTransferAllowed(_from, _amount) { executeTransferInternal(_from, _to, _amount); } /** * @dev redirects the interest generated to a target address. * when the interest is redirected, the user balance is added to * the recepient redirected balance. * @param _to the address to which the interest will be redirected **/ function redirectInterestStream(address _to) external { redirectInterestStreamInternal(msg.sender, _to); } /** * @dev redirects the interest generated by _from to a target address. * when the interest is redirected, the user balance is added to * the recepient redirected balance. The caller needs to have allowance on * the interest redirection to be able to execute the function. * @param _from the address of the user whom interest is being redirected * @param _to the address to which the interest will be redirected **/ function redirectInterestStreamOf(address _from, address _to) external { require( msg.sender == interestRedirectionAllowances[_from], "Caller is not allowed to redirect the interest of the user" ); redirectInterestStreamInternal(_from,_to); } /** * @dev gives allowance to an address to execute the interest redirection * on behalf of the caller. * @param _to the address to which the interest will be redirected. Pass address(0) to reset * the allowance. **/ function allowInterestRedirectionTo(address _to) external { require(_to != msg.sender, "User cannot give allowance to himself"); interestRedirectionAllowances[msg.sender] = _to; emit InterestRedirectionAllowanceChanged( msg.sender, _to ); } /** * @dev redeems aToken for the underlying asset * @param _amount the amount being redeemed **/ function redeem(uint256 _amount) external { require(_amount > 0, "Amount to redeem needs to be > 0"); //cumulates the balance of the user (, uint256 currentBalance, uint256 balanceIncrease, uint256 index) = cumulateBalanceInternal(msg.sender); uint256 amountToRedeem = _amount; //if amount is equal to uint(-1), the user wants to redeem everything if(_amount == UINT_MAX_VALUE){ amountToRedeem = currentBalance; } require(amountToRedeem <= currentBalance, "User cannot redeem more than the available balance"); //check that the user is allowed to redeem the amount require(isTransferAllowed(msg.sender, amountToRedeem), "Transfer cannot be allowed."); //if the user is redirecting his interest towards someone else, //we update the redirected balance of the redirection address by adding the accrued interest, //and removing the amount to redeem updateRedirectedBalanceOfRedirectionAddressInternal(msg.sender, balanceIncrease, amountToRedeem); // burns tokens equivalent to the amount requested _burn(msg.sender, amountToRedeem); bool userIndexReset = false; //reset the user data if the remaining balance is 0 if(currentBalance.sub(amountToRedeem) == 0){ userIndexReset = resetDataOnZeroBalanceInternal(msg.sender); } // executes redeem of the underlying asset pool.redeemUnderlying( underlyingAssetAddress, msg.sender, amountToRedeem, currentBalance.sub(amountToRedeem) ); emit Redeem(msg.sender, amountToRedeem, balanceIncrease, userIndexReset ? 0 : index); } /** * @dev mints token in the event of users depositing the underlying asset into the lending pool * only lending pools can call this function * @param _account the address receiving the minted tokens * @param _amount the amount of tokens to mint */ function mintOnDeposit(address _account, uint256 _amount) external onlyLendingPool { //cumulates the balance of the user (, , uint256 balanceIncrease, uint256 index) = cumulateBalanceInternal(_account); //if the user is redirecting his interest towards someone else, //we update the redirected balance of the redirection address by adding the accrued interest //and the amount deposited updateRedirectedBalanceOfRedirectionAddressInternal(_account, balanceIncrease.add(_amount), 0); //mint an equivalent amount of tokens to cover the new deposit _mint(_account, _amount); emit MintOnDeposit(_account, _amount, balanceIncrease, index); } /** * @dev burns token in the event of a borrow being liquidated, in case the liquidators reclaims the underlying asset * Transfer of the liquidated asset is executed by the lending pool contract. * only lending pools can call this function * @param _account the address from which burn the aTokens * @param _value the amount to burn **/ function burnOnLiquidation(address _account, uint256 _value) external onlyLendingPool { //cumulates the balance of the user being liquidated (,uint256 accountBalance,uint256 balanceIncrease,uint256 index) = cumulateBalanceInternal(_account); //adds the accrued interest and substracts the burned amount to //the redirected balance updateRedirectedBalanceOfRedirectionAddressInternal(_account, balanceIncrease, _value); //burns the requested amount of tokens _burn(_account, _value); bool userIndexReset = false; //reset the user data if the remaining balance is 0 if(accountBalance.sub(_value) == 0){ userIndexReset = resetDataOnZeroBalanceInternal(_account); } emit BurnOnLiquidation(_account, _value, balanceIncrease, userIndexReset ? 0 : index); } /** * @dev transfers tokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken * only lending pools can call this function * @param _from the address from which transfer the aTokens * @param _to the destination address * @param _value the amount to transfer **/ function transferOnLiquidation(address _from, address _to, uint256 _value) external onlyLendingPool { //being a normal transfer, the Transfer() and BalanceTransfer() are emitted //so no need to emit a specific event here executeTransferInternal(_from, _to, _value); } /** * @dev calculates the balance of the user, which is the * principal balance + interest generated by the principal balance + interest generated by the redirected balance * @param _user the user for which the balance is being calculated * @return the total balance of the user **/ function balanceOf(address _user) public view returns(uint256) { //current principal balance of the user uint256 currentPrincipalBalance = super.balanceOf(_user); //balance redirected by other users to _user for interest rate accrual uint256 redirectedBalance = redirectedBalances[_user]; if(currentPrincipalBalance == 0 && redirectedBalance == 0){ return 0; } //if the _user is not redirecting the interest to anybody, accrues //the interest for himself if(interestRedirectionAddresses[_user] == address(0)){ //accruing for himself means that both the principal balance and //the redirected balance partecipate in the interest return calculateCumulatedBalanceInternal( _user, currentPrincipalBalance.add(redirectedBalance) ) .sub(redirectedBalance); } else { //if the user redirected the interest, then only the redirected //balance generates interest. In that case, the interest generated //by the redirected balance is added to the current principal balance. return currentPrincipalBalance.add( calculateCumulatedBalanceInternal( _user, redirectedBalance ) .sub(redirectedBalance) ); } } /** * @dev returns the principal balance of the user. The principal balance is the last * updated stored balance, which does not consider the perpetually accruing interest. * @param _user the address of the user * @return the principal balance of the user **/ function principalBalanceOf(address _user) external view returns(uint256) { return super.balanceOf(_user); } /** * @dev calculates the total supply of the specific aToken * since the balance of every single user increases over time, the total supply * does that too. * @return the current total supply **/ function totalSupply() public view returns(uint256) { uint256 currentSupplyPrincipal = super.totalSupply(); if(currentSupplyPrincipal == 0){ return 0; } return currentSupplyPrincipal .wadToRay() .rayMul(core.getReserveNormalizedIncome(underlyingAssetAddress)) .rayToWad(); } /** * @dev Used to validate transfers before actually executing them. * @param _user address of the user to check * @param _amount the amount to check * @return true if the _user can transfer _amount, false otherwise **/ function isTransferAllowed(address _user, uint256 _amount) public view returns (bool) { return dataProvider.balanceDecreaseAllowed(underlyingAssetAddress, _user, _amount); } /** * @dev returns the last index of the user, used to calculate the balance of the user * @param _user address of the user * @return the last user index **/ function getUserIndex(address _user) external view returns(uint256) { return userIndexes[_user]; } /** * @dev returns the address to which the interest is redirected * @param _user address of the user * @return 0 if there is no redirection, an address otherwise **/ function getInterestRedirectionAddress(address _user) external view returns(address) { return interestRedirectionAddresses[_user]; } /** * @dev returns the redirected balance of the user. The redirected balance is the balance * redirected by other accounts to the user, that is accrueing interest for him. * @param _user address of the user * @return the total redirected balance **/ function getRedirectedBalance(address _user) external view returns(uint256) { return redirectedBalances[_user]; } /** * @dev accumulates the accrued interest of the user to the principal balance * @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 cumulateBalanceInternal(address _user) internal returns(uint256, uint256, uint256, uint256) { uint256 previousPrincipalBalance = super.balanceOf(_user); //calculate the accrued interest since the last accumulation uint256 balanceIncrease = balanceOf(_user).sub(previousPrincipalBalance); //mints an amount of tokens equivalent to the amount accumulated _mint(_user, balanceIncrease); //updates the user index uint256 index = userIndexes[_user] = core.getReserveNormalizedIncome(underlyingAssetAddress); return ( previousPrincipalBalance, previousPrincipalBalance.add(balanceIncrease), balanceIncrease, index ); } /** * @dev updates the redirected balance of the user. If the user is not redirecting his * interest, nothing is executed. * @param _user the address of the user for which the interest is being accumulated * @param _balanceToAdd the amount to add to the redirected balance * @param _balanceToRemove the amount to remove from the redirected balance **/ function updateRedirectedBalanceOfRedirectionAddressInternal( address _user, uint256 _balanceToAdd, uint256 _balanceToRemove ) internal { address redirectionAddress = interestRedirectionAddresses[_user]; //if there isn't any redirection, nothing to be done if(redirectionAddress == address(0)){ return; } //compound balances of the redirected address (,,uint256 balanceIncrease, uint256 index) = cumulateBalanceInternal(redirectionAddress); //updating the redirected balance redirectedBalances[redirectionAddress] = redirectedBalances[redirectionAddress] .add(_balanceToAdd) .sub(_balanceToRemove); //if the interest of redirectionAddress is also being redirected, we need to update //the redirected balance of the redirection target by adding the balance increase address targetOfRedirectionAddress = interestRedirectionAddresses[redirectionAddress]; if(targetOfRedirectionAddress != address(0)){ redirectedBalances[targetOfRedirectionAddress] = redirectedBalances[targetOfRedirectionAddress].add(balanceIncrease); } emit RedirectedBalanceUpdated( redirectionAddress, balanceIncrease, index, _balanceToAdd, _balanceToRemove ); } /** * @dev calculate the interest accrued by _user on a specific balance * @param _user the address of the user for which the interest is being accumulated * @param _balance the balance on which the interest is calculated * @return the interest rate accrued **/ function calculateCumulatedBalanceInternal( address _user, uint256 _balance ) internal view returns (uint256) { return _balance .wadToRay() .rayMul(core.getReserveNormalizedIncome(underlyingAssetAddress)) .rayDiv(userIndexes[_user]) .rayToWad(); } /** * @dev executes the transfer of aTokens, invoked by both _transfer() and * transferOnLiquidation() * @param _from the address from which transfer the aTokens * @param _to the destination address * @param _value the amount to transfer **/ function executeTransferInternal( address _from, address _to, uint256 _value ) internal { require(_value > 0, "Transferred amount needs to be greater than zero"); //cumulate the balance of the sender (, uint256 fromBalance, uint256 fromBalanceIncrease, uint256 fromIndex ) = cumulateBalanceInternal(_from); //cumulate the balance of the receiver (, , uint256 toBalanceIncrease, uint256 toIndex ) = cumulateBalanceInternal(_to); //if the sender is redirecting his interest towards someone else, //adds to the redirected balance the accrued interest and removes the amount //being transferred updateRedirectedBalanceOfRedirectionAddressInternal(_from, fromBalanceIncrease, _value); //if the receiver is redirecting his interest towards someone else, //adds to the redirected balance the accrued interest and the amount //being transferred updateRedirectedBalanceOfRedirectionAddressInternal(_to, toBalanceIncrease.add(_value), 0); //performs the transfer super._transfer(_from, _to, _value); bool fromIndexReset = false; //reset the user data if the remaining balance is 0 if(fromBalance.sub(_value) == 0){ fromIndexReset = resetDataOnZeroBalanceInternal(_from); } emit BalanceTransfer( _from, _to, _value, fromBalanceIncrease, toBalanceIncrease, fromIndexReset ? 0 : fromIndex, toIndex ); } /** * @dev executes the redirection of the interest from one address to another. * immediately after redirection, the destination address will start to accrue interest. * @param _from the address from which transfer the aTokens * @param _to the destination address **/ function redirectInterestStreamInternal( address _from, address _to ) internal { address currentRedirectionAddress = interestRedirectionAddresses[_from]; require(_to != currentRedirectionAddress, "Interest is already redirected to the user"); //accumulates the accrued interest to the principal (uint256 previousPrincipalBalance, uint256 fromBalance, uint256 balanceIncrease, uint256 fromIndex) = cumulateBalanceInternal(_from); require(fromBalance > 0, "Interest stream can only be redirected if there is a valid balance"); //if the user is already redirecting the interest to someone, before changing //the redirection address we substract the redirected balance of the previous //recipient if(currentRedirectionAddress != address(0)){ updateRedirectedBalanceOfRedirectionAddressInternal(_from,0, previousPrincipalBalance); } //if the user is redirecting the interest back to himself, //we simply set to 0 the interest redirection address if(_to == _from) { interestRedirectionAddresses[_from] = address(0); emit InterestStreamRedirected( _from, address(0), fromBalance, balanceIncrease, fromIndex ); return; } //first set the redirection address to the new recipient interestRedirectionAddresses[_from] = _to; //adds the user balance to the redirected balance of the destination updateRedirectedBalanceOfRedirectionAddressInternal(_from,fromBalance,0); emit InterestStreamRedirected( _from, _to, fromBalance, balanceIncrease, fromIndex ); } /** * @dev function to reset the interest stream redirection and the user index, if the * user has no balance left. * @param _user the address of the user * @return true if the user index has also been reset, false otherwise. useful to emit the proper user index value **/ function resetDataOnZeroBalanceInternal(address _user) internal returns(bool) { //if the user has 0 principal balance, the interest stream redirection gets reset interestRedirectionAddresses[_user] = address(0); //emits a InterestStreamRedirected event to notify that the redirection has been reset emit InterestStreamRedirected(_user, address(0),0,0,0); //if the redirected balance is also 0, we clear up the user index if(redirectedBalances[_user] == 0){ userIndexes[_user] = 0; return true; } else{ return false; } } } /** * @title IFlashLoanReceiver interface * @notice Interface for the Aave fee IFlashLoanReceiver. * @author Aave * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract **/ interface IFlashLoanReceiver { function executeOperation(address _reserve, uint256 _amount, uint256 _fee, bytes calldata _params) external; } /** * @title ILendingRateOracle interface * @notice Interface for the Aave borrow rate oracle. Provides the average market borrow rate to be used as a base for the stable borrow rate calculations **/ interface ILendingRateOracle { /** @dev returns the market borrow rate in ray **/ function getMarketBorrowRate(address _asset) external view returns (uint256); /** @dev sets the market borrow rate. Rate value must be in ray **/ function setMarketBorrowRate(address _asset, uint256 _rate) external; } /** @title IReserveInterestRateStrategyInterface interface @notice Interface for the calculation of the interest rates. */ interface IReserveInterestRateStrategy { /** * @dev returns the base variable borrow rate, in rays */ function getBaseVariableBorrowRate() external view returns (uint256); /** * @dev calculates the liquidity, stable, and variable rates depending on the current utilization rate * and the base parameters * */ function calculateInterestRates( address _reserve, uint256 _utilizationRate, uint256 _totalBorrowsStable, uint256 _totalBorrowsVariable, uint256 _averageStableBorrowRate) external view returns (uint256 liquidityRate, uint256 stableBorrowRate, uint256 variableBorrowRate); } library EthAddressLib { /** * @dev returns the address used within the protocol to identify ETH * @return the address assigned to ETH */ function ethAddress() internal pure returns(address) { return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; } } /** * @title LendingPoolCore contract * @author Aave * @notice Holds the state of the lending pool and all the funds deposited * @dev NOTE: The core does not enforce security checks on the update of the state * (eg, updateStateOnBorrow() does not enforce that borrowed is enabled on the reserve). * The check that an action can be performed is a duty of the overlying LendingPool contract. **/ contract LendingPoolCore is VersionedInitializable { using SafeMath for uint256; using WadRayMath for uint256; using CoreLibrary for CoreLibrary.ReserveData; using CoreLibrary for CoreLibrary.UserReserveData; using SafeERC20 for ERC20; using Address for address payable; /** * @dev Emitted when the state of a reserve is updated * @param reserve the address of the reserve * @param liquidityRate the new liquidity rate * @param stableBorrowRate the new stable borrow rate * @param variableBorrowRate the new variable borrow rate * @param liquidityIndex the new liquidity index * @param variableBorrowIndex the new variable borrow index **/ event ReserveUpdated( address indexed reserve, uint256 liquidityRate, uint256 stableBorrowRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex ); address public lendingPoolAddress; LendingPoolAddressesProvider public addressesProvider; /** * @dev only lending pools can use functions affected by this modifier **/ modifier onlyLendingPool { require(lendingPoolAddress == msg.sender, "The caller must be a lending pool contract"); _; } /** * @dev only lending pools configurator can use functions affected by this modifier **/ modifier onlyLendingPoolConfigurator { require( addressesProvider.getLendingPoolConfigurator() == msg.sender, "The caller must be a lending pool configurator contract" ); _; } mapping(address => CoreLibrary.ReserveData) internal reserves; mapping(address => mapping(address => CoreLibrary.UserReserveData)) internal usersReserveData; address[] public reservesList; uint256 public constant CORE_REVISION = 0x4; /** * @dev returns the revision number of the contract **/ function getRevision() internal pure returns (uint256) { return CORE_REVISION; } /** * @dev initializes the Core contract, invoked upon registration on the AddressesProvider * @param _addressesProvider the addressesProvider contract **/ function initialize(LendingPoolAddressesProvider _addressesProvider) public initializer { addressesProvider = _addressesProvider; refreshConfigInternal(); } /** * @dev updates the state of the core as a result of a deposit action * @param _reserve the address of the reserve in which the deposit is happening * @param _user the address of the the user depositing * @param _amount the amount being deposited * @param _isFirstDeposit true if the user is depositing for the first time **/ function updateStateOnDeposit( address _reserve, address _user, uint256 _amount, bool _isFirstDeposit ) external onlyLendingPool { reserves[_reserve].updateCumulativeIndexes(); updateReserveInterestRatesAndTimestampInternal(_reserve, _amount, 0); if (_isFirstDeposit) { //if this is the first deposit of the user, we configure the deposit as enabled to be used as collateral setUserUseReserveAsCollateral(_reserve, _user, true); } } /** * @dev updates the state of the core as a result of a redeem action * @param _reserve the address of the reserve in which the redeem is happening * @param _user the address of the the user redeeming * @param _amountRedeemed the amount being redeemed * @param _userRedeemedEverything true if the user is redeeming everything **/ function updateStateOnRedeem( address _reserve, address _user, uint256 _amountRedeemed, bool _userRedeemedEverything ) external onlyLendingPool { //compound liquidity and variable borrow interests reserves[_reserve].updateCumulativeIndexes(); updateReserveInterestRatesAndTimestampInternal(_reserve, 0, _amountRedeemed); //if user redeemed everything the useReserveAsCollateral flag is reset if (_userRedeemedEverything) { setUserUseReserveAsCollateral(_reserve, _user, false); } } /** * @dev updates the state of the core as a result of a flashloan action * @param _reserve the address of the reserve in which the flashloan is happening * @param _income the income of the protocol as a result of the action **/ function updateStateOnFlashLoan( address _reserve, uint256 _availableLiquidityBefore, uint256 _income, uint256 _protocolFee ) external onlyLendingPool { transferFlashLoanProtocolFeeInternal(_reserve, _protocolFee); //compounding the cumulated interest reserves[_reserve].updateCumulativeIndexes(); uint256 totalLiquidityBefore = _availableLiquidityBefore.add( getReserveTotalBorrows(_reserve) ); //compounding the received fee into the reserve reserves[_reserve].cumulateToLiquidityIndex(totalLiquidityBefore, _income); //refresh interest rates updateReserveInterestRatesAndTimestampInternal(_reserve, _income, 0); } /** * @dev updates the state of the core as a consequence of a borrow action. * @param _reserve the address of the reserve on which the user is borrowing * @param _user the address of the borrower * @param _amountBorrowed the new amount borrowed * @param _borrowFee the fee on the amount borrowed * @param _rateMode the borrow rate mode (stable, variable) * @return the new borrow rate for the user **/ function updateStateOnBorrow( address _reserve, address _user, uint256 _amountBorrowed, uint256 _borrowFee, CoreLibrary.InterestRateMode _rateMode ) external onlyLendingPool returns (uint256, uint256) { // getting the previous borrow data of the user (uint256 principalBorrowBalance, , uint256 balanceIncrease) = getUserBorrowBalances( _reserve, _user ); updateReserveStateOnBorrowInternal( _reserve, _user, principalBorrowBalance, balanceIncrease, _amountBorrowed, _rateMode ); updateUserStateOnBorrowInternal( _reserve, _user, _amountBorrowed, balanceIncrease, _borrowFee, _rateMode ); updateReserveInterestRatesAndTimestampInternal(_reserve, 0, _amountBorrowed); return (getUserCurrentBorrowRate(_reserve, _user), balanceIncrease); } /** * @dev updates the state of the core as a consequence of a repay action. * @param _reserve the address of the reserve on which the user is repaying * @param _user the address of the borrower * @param _paybackAmountMinusFees the amount being paid back minus fees * @param _originationFeeRepaid the fee on the amount that is being repaid * @param _balanceIncrease the accrued interest on the borrowed amount * @param _repaidWholeLoan true if the user is repaying the whole loan **/ function updateStateOnRepay( address _reserve, address _user, uint256 _paybackAmountMinusFees, uint256 _originationFeeRepaid, uint256 _balanceIncrease, bool _repaidWholeLoan ) external onlyLendingPool { updateReserveStateOnRepayInternal( _reserve, _user, _paybackAmountMinusFees, _balanceIncrease ); updateUserStateOnRepayInternal( _reserve, _user, _paybackAmountMinusFees, _originationFeeRepaid, _balanceIncrease, _repaidWholeLoan ); updateReserveInterestRatesAndTimestampInternal(_reserve, _paybackAmountMinusFees, 0); } /** * @dev updates the state of the core as a consequence of a swap rate action. * @param _reserve the address of the reserve on which the user is repaying * @param _user the address of the borrower * @param _principalBorrowBalance the amount borrowed by the user * @param _compoundedBorrowBalance the amount borrowed plus accrued interest * @param _balanceIncrease the accrued interest on the borrowed amount * @param _currentRateMode the current interest rate mode for the user **/ function updateStateOnSwapRate( address _reserve, address _user, uint256 _principalBorrowBalance, uint256 _compoundedBorrowBalance, uint256 _balanceIncrease, CoreLibrary.InterestRateMode _currentRateMode ) external onlyLendingPool returns (CoreLibrary.InterestRateMode, uint256) { updateReserveStateOnSwapRateInternal( _reserve, _user, _principalBorrowBalance, _compoundedBorrowBalance, _currentRateMode ); CoreLibrary.InterestRateMode newRateMode = updateUserStateOnSwapRateInternal( _reserve, _user, _balanceIncrease, _currentRateMode ); updateReserveInterestRatesAndTimestampInternal(_reserve, 0, 0); return (newRateMode, getUserCurrentBorrowRate(_reserve, _user)); } /** * @dev updates the state of the core as a consequence of a liquidation action. * @param _principalReserve the address of the principal reserve that is being repaid * @param _collateralReserve the address of the collateral reserve that is being liquidated * @param _user the address of the borrower * @param _amountToLiquidate the amount being repaid by the liquidator * @param _collateralToLiquidate the amount of collateral being liquidated * @param _feeLiquidated the amount of origination fee being liquidated * @param _liquidatedCollateralForFee the amount of collateral equivalent to the origination fee + bonus * @param _balanceIncrease the accrued interest on the borrowed amount * @param _liquidatorReceivesAToken true if the liquidator will receive aTokens, false otherwise **/ function updateStateOnLiquidation( address _principalReserve, address _collateralReserve, address _user, uint256 _amountToLiquidate, uint256 _collateralToLiquidate, uint256 _feeLiquidated, uint256 _liquidatedCollateralForFee, uint256 _balanceIncrease, bool _liquidatorReceivesAToken ) external onlyLendingPool { updatePrincipalReserveStateOnLiquidationInternal( _principalReserve, _user, _amountToLiquidate, _balanceIncrease ); updateCollateralReserveStateOnLiquidationInternal( _collateralReserve ); updateUserStateOnLiquidationInternal( _principalReserve, _user, _amountToLiquidate, _feeLiquidated, _balanceIncrease ); updateReserveInterestRatesAndTimestampInternal(_principalReserve, _amountToLiquidate, 0); if (!_liquidatorReceivesAToken) { updateReserveInterestRatesAndTimestampInternal( _collateralReserve, 0, _collateralToLiquidate.add(_liquidatedCollateralForFee) ); } } /** * @dev updates the state of the core as a consequence of a stable rate rebalance * @param _reserve the address of the principal reserve where the user borrowed * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount * @return the new stable rate for the user **/ function updateStateOnRebalance(address _reserve, address _user, uint256 _balanceIncrease) external onlyLendingPool returns (uint256) { updateReserveStateOnRebalanceInternal(_reserve, _user, _balanceIncrease); //update user data and rebalance the rate updateUserStateOnRebalanceInternal(_reserve, _user, _balanceIncrease); updateReserveInterestRatesAndTimestampInternal(_reserve, 0, 0); return usersReserveData[_user][_reserve].stableBorrowRate; } /** * @dev enables or disables a reserve as collateral * @param _reserve the address of the principal reserve where the user deposited * @param _user the address of the depositor * @param _useAsCollateral true if the depositor wants to use the reserve as collateral **/ function setUserUseReserveAsCollateral(address _reserve, address _user, bool _useAsCollateral) public onlyLendingPool { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; user.useAsCollateral = _useAsCollateral; } /** * @notice ETH/token transfer functions **/ /** * @dev fallback function enforces that the caller is a contract, to support flashloan transfers **/ function() external payable { //only contracts can send ETH to the core require(msg.sender.isContract(), "Only contracts can send ether to the Lending pool core"); } /** * @dev transfers to the user a specific amount from the reserve. * @param _reserve the address of the reserve where the transfer is happening * @param _user the address of the user receiving the transfer * @param _amount the amount being transferred **/ function transferToUser(address _reserve, address payable _user, uint256 _amount) external onlyLendingPool { if (_reserve != EthAddressLib.ethAddress()) { ERC20(_reserve).safeTransfer(_user, _amount); } else { //solium-disable-next-line (bool result, ) = _user.call.value(_amount).gas(50000)(""); require(result, "Transfer of ETH failed"); } } /** * @dev transfers the protocol fees to the fees collection address * @param _token the address of the token being transferred * @param _user the address of the user from where the transfer is happening * @param _amount the amount being transferred * @param _destination the fee receiver address **/ function transferToFeeCollectionAddress( address _token, address _user, uint256 _amount, address _destination ) external payable onlyLendingPool { address payable feeAddress = address(uint160(_destination)); //cast the address to payable if (_token != EthAddressLib.ethAddress()) { require( msg.value == 0, "User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction" ); ERC20(_token).safeTransferFrom(_user, feeAddress, _amount); } else { require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); //solium-disable-next-line (bool result, ) = feeAddress.call.value(_amount).gas(50000)(""); require(result, "Transfer of ETH failed"); } } /** * @dev transfers the fees to the fees collection address in the case of liquidation * @param _token the address of the token being transferred * @param _amount the amount being transferred * @param _destination the fee receiver address **/ function liquidateFee( address _token, uint256 _amount, address _destination ) external payable onlyLendingPool { address payable feeAddress = address(uint160(_destination)); //cast the address to payable require( msg.value == 0, "Fee liquidation does not require any transfer of value" ); if (_token != EthAddressLib.ethAddress()) { ERC20(_token).safeTransfer(feeAddress, _amount); } else { //solium-disable-next-line (bool result, ) = feeAddress.call.value(_amount).gas(50000)(""); require(result, "Transfer of ETH failed"); } } /** * @dev transfers an amount from a user to the destination reserve * @param _reserve the address of the reserve where the amount is being transferred * @param _user the address of the user from where the transfer is happening * @param _amount the amount being transferred **/ function transferToReserve(address _reserve, address payable _user, uint256 _amount) external payable onlyLendingPool { if (_reserve != EthAddressLib.ethAddress()) { require(msg.value == 0, "User is sending ETH along with the ERC20 transfer."); ERC20(_reserve).safeTransferFrom(_user, address(this), _amount); } else { require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); if (msg.value > _amount) { //send back excess ETH uint256 excessAmount = msg.value.sub(_amount); //solium-disable-next-line (bool result, ) = _user.call.value(excessAmount).gas(50000)(""); require(result, "Transfer of ETH failed"); } } } /** * @notice data access functions **/ /** * @dev returns the basic data (balances, fee accrued, reserve enabled/disabled as collateral) * needed to calculate the global account data in the LendingPoolDataProvider * @param _reserve the address of the reserve * @param _user the address of the user * @return the user deposited balance, the principal borrow balance, the fee, and if the reserve is enabled as collateral or not **/ function getUserBasicReserveData(address _reserve, address _user) external view returns (uint256, uint256, uint256, bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; uint256 underlyingBalance = getUserUnderlyingAssetBalance(_reserve, _user); if (user.principalBorrowBalance == 0) { return (underlyingBalance, 0, 0, user.useAsCollateral); } return ( underlyingBalance, user.getCompoundedBorrowBalance(reserve), user.originationFee, user.useAsCollateral ); } /** * @dev checks if a user is allowed to borrow at a stable rate * @param _reserve the reserve address * @param _user the user * @param _amount the amount the the user wants to borrow * @return true if the user is allowed to borrow at a stable rate, false otherwise **/ function isUserAllowedToBorrowAtStable(address _reserve, address _user, uint256 _amount) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; if (!reserve.isStableBorrowRateEnabled) return false; return !user.useAsCollateral || !reserve.usageAsCollateralEnabled || _amount > getUserUnderlyingAssetBalance(_reserve, _user); } /** * @dev gets the underlying asset balance of a user based on the corresponding aToken balance. * @param _reserve the reserve address * @param _user the user address * @return the underlying deposit balance of the user **/ function getUserUnderlyingAssetBalance(address _reserve, address _user) public view returns (uint256) { AToken aToken = AToken(reserves[_reserve].aTokenAddress); return aToken.balanceOf(_user); } /** * @dev gets the interest rate strategy contract address for the reserve * @param _reserve the reserve address * @return the address of the interest rate strategy contract **/ function getReserveInterestRateStrategyAddress(address _reserve) public view returns (address) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.interestRateStrategyAddress; } /** * @dev gets the aToken contract address for the reserve * @param _reserve the reserve address * @return the address of the aToken contract **/ function getReserveATokenAddress(address _reserve) public view returns (address) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.aTokenAddress; } /** * @dev gets the available liquidity in the reserve. The available liquidity is the balance of the core contract * @param _reserve the reserve address * @return the available liquidity **/ function getReserveAvailableLiquidity(address _reserve) public view returns (uint256) { uint256 balance = 0; if (_reserve == EthAddressLib.ethAddress()) { balance = address(this).balance; } else { balance = IERC20(_reserve).balanceOf(address(this)); } return balance; } /** * @dev gets the total liquidity in the reserve. The total liquidity is the balance of the core contract + total borrows * @param _reserve the reserve address * @return the total liquidity **/ function getReserveTotalLiquidity(address _reserve) public view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return getReserveAvailableLiquidity(_reserve).add(reserve.getTotalBorrows()); } /** * @dev gets the normalized income of the reserve. a value of 1e27 means there is no income. A value of 2e27 means there * there has been 100% income. * @param _reserve the reserve address * @return the reserve normalized income **/ function getReserveNormalizedIncome(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.getNormalizedIncome(); } /** * @dev gets the reserve total borrows * @param _reserve the reserve address * @return the total borrows (stable + variable) **/ function getReserveTotalBorrows(address _reserve) public view returns (uint256) { return reserves[_reserve].getTotalBorrows(); } /** * @dev gets the reserve total borrows stable * @param _reserve the reserve address * @return the total borrows stable **/ function getReserveTotalBorrowsStable(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.totalBorrowsStable; } /** * @dev gets the reserve total borrows variable * @param _reserve the reserve address * @return the total borrows variable **/ function getReserveTotalBorrowsVariable(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.totalBorrowsVariable; } /** * @dev gets the reserve liquidation threshold * @param _reserve the reserve address * @return the reserve liquidation threshold **/ function getReserveLiquidationThreshold(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.liquidationThreshold; } /** * @dev gets the reserve liquidation bonus * @param _reserve the reserve address * @return the reserve liquidation bonus **/ function getReserveLiquidationBonus(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.liquidationBonus; } /** * @dev gets the reserve current variable borrow rate. Is the base variable borrow rate if the reserve is empty * @param _reserve the reserve address * @return the reserve current variable borrow rate **/ function getReserveCurrentVariableBorrowRate(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; if (reserve.currentVariableBorrowRate == 0) { return IReserveInterestRateStrategy(reserve.interestRateStrategyAddress) .getBaseVariableBorrowRate(); } return reserve.currentVariableBorrowRate; } /** * @dev gets the reserve current stable borrow rate. Is the market rate if the reserve is empty * @param _reserve the reserve address * @return the reserve current stable borrow rate **/ function getReserveCurrentStableBorrowRate(address _reserve) public view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; ILendingRateOracle oracle = ILendingRateOracle(addressesProvider.getLendingRateOracle()); if (reserve.currentStableBorrowRate == 0) { //no stable rate borrows yet return oracle.getMarketBorrowRate(_reserve); } return reserve.currentStableBorrowRate; } /** * @dev gets the reserve average stable borrow rate. The average stable rate is the weighted average * of all the loans taken at stable rate. * @param _reserve the reserve address * @return the reserve current average borrow rate **/ function getReserveCurrentAverageStableBorrowRate(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.currentAverageStableBorrowRate; } /** * @dev gets the reserve liquidity rate * @param _reserve the reserve address * @return the reserve liquidity rate **/ function getReserveCurrentLiquidityRate(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.currentLiquidityRate; } /** * @dev gets the reserve liquidity cumulative index * @param _reserve the reserve address * @return the reserve liquidity cumulative index **/ function getReserveLiquidityCumulativeIndex(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.lastLiquidityCumulativeIndex; } /** * @dev gets the reserve variable borrow index * @param _reserve the reserve address * @return the reserve variable borrow index **/ function getReserveVariableBorrowsCumulativeIndex(address _reserve) external view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.lastVariableBorrowCumulativeIndex; } /** * @dev this function aggregates the configuration parameters of the reserve. * It's used in the LendingPoolDataProvider specifically to save gas, and avoid * multiple external contract calls to fetch the same data. * @param _reserve the reserve address * @return the reserve decimals * @return the base ltv as collateral * @return the liquidation threshold * @return if the reserve is used as collateral or not **/ function getReserveConfiguration(address _reserve) external view returns (uint256, uint256, uint256, bool) { uint256 decimals; uint256 baseLTVasCollateral; uint256 liquidationThreshold; bool usageAsCollateralEnabled; CoreLibrary.ReserveData storage reserve = reserves[_reserve]; decimals = reserve.decimals; baseLTVasCollateral = reserve.baseLTVasCollateral; liquidationThreshold = reserve.liquidationThreshold; usageAsCollateralEnabled = reserve.usageAsCollateralEnabled; return (decimals, baseLTVasCollateral, liquidationThreshold, usageAsCollateralEnabled); } /** * @dev returns the decimals of the reserve * @param _reserve the reserve address * @return the reserve decimals **/ function getReserveDecimals(address _reserve) external view returns (uint256) { return reserves[_reserve].decimals; } /** * @dev returns true if the reserve is enabled for borrowing * @param _reserve the reserve address * @return true if the reserve is enabled for borrowing, false otherwise **/ function isReserveBorrowingEnabled(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.borrowingEnabled; } /** * @dev returns true if the reserve is enabled as collateral * @param _reserve the reserve address * @return true if the reserve is enabled as collateral, false otherwise **/ function isReserveUsageAsCollateralEnabled(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.usageAsCollateralEnabled; } /** * @dev returns true if the stable rate is enabled on reserve * @param _reserve the reserve address * @return true if the stable rate is enabled on reserve, false otherwise **/ function getReserveIsStableBorrowRateEnabled(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.isStableBorrowRateEnabled; } /** * @dev returns true if the reserve is active * @param _reserve the reserve address * @return true if the reserve is active, false otherwise **/ function getReserveIsActive(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.isActive; } /** * @notice returns if a reserve is freezed * @param _reserve the reserve for which the information is needed * @return true if the reserve is freezed, false otherwise **/ function getReserveIsFreezed(address _reserve) external view returns (bool) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.isFreezed; } /** * @notice returns the timestamp of the last action on the reserve * @param _reserve the reserve for which the information is needed * @return the last updated timestamp of the reserve **/ function getReserveLastUpdate(address _reserve) external view returns (uint40 timestamp) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; timestamp = reserve.lastUpdateTimestamp; } /** * @dev returns the utilization rate U of a specific reserve * @param _reserve the reserve for which the information is needed * @return the utilization rate in ray **/ function getReserveUtilizationRate(address _reserve) public view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; uint256 totalBorrows = reserve.getTotalBorrows(); if (totalBorrows == 0) { return 0; } uint256 availableLiquidity = getReserveAvailableLiquidity(_reserve); return totalBorrows.rayDiv(availableLiquidity.add(totalBorrows)); } /** * @return the array of reserves configured on the core **/ function getReserves() external view returns (address[] memory) { return reservesList; } /** * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return true if the user has chosen to use the reserve as collateral, false otherwise **/ function isUserUseReserveAsCollateralEnabled(address _reserve, address _user) external view returns (bool) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; return user.useAsCollateral; } /** * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the origination fee for the user **/ function getUserOriginationFee(address _reserve, address _user) external view returns (uint256) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; return user.originationFee; } /** * @dev users with no loans in progress have NONE as borrow rate mode * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the borrow rate mode for the user, **/ function getUserCurrentBorrowRateMode(address _reserve, address _user) public view returns (CoreLibrary.InterestRateMode) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; if (user.principalBorrowBalance == 0) { return CoreLibrary.InterestRateMode.NONE; } return user.stableBorrowRate > 0 ? CoreLibrary.InterestRateMode.STABLE : CoreLibrary.InterestRateMode.VARIABLE; } /** * @dev gets the current borrow rate of the user * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the borrow rate for the user, **/ function getUserCurrentBorrowRate(address _reserve, address _user) internal view returns (uint256) { CoreLibrary.InterestRateMode rateMode = getUserCurrentBorrowRateMode(_reserve, _user); if (rateMode == CoreLibrary.InterestRateMode.NONE) { return 0; } return rateMode == CoreLibrary.InterestRateMode.STABLE ? usersReserveData[_user][_reserve].stableBorrowRate : reserves[_reserve].currentVariableBorrowRate; } /** * @dev the stable rate returned is 0 if the user is borrowing at variable or not borrowing at all * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the user stable rate **/ function getUserCurrentStableBorrowRate(address _reserve, address _user) external view returns (uint256) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; return user.stableBorrowRate; } /** * @dev calculates and returns the borrow balances of the user * @param _reserve the address of the reserve * @param _user the address of the user * @return the principal borrow balance, the compounded balance and the balance increase since the last borrow/repay/swap/rebalance **/ function getUserBorrowBalances(address _reserve, address _user) public view returns (uint256, uint256, uint256) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; if (user.principalBorrowBalance == 0) { return (0, 0, 0); } uint256 principal = user.principalBorrowBalance; uint256 compoundedBalance = CoreLibrary.getCompoundedBorrowBalance( user, reserves[_reserve] ); return (principal, compoundedBalance, compoundedBalance.sub(principal)); } /** * @dev the variable borrow index of the user is 0 if the user is not borrowing or borrowing at stable * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the variable borrow index for the user **/ function getUserVariableBorrowCumulativeIndex(address _reserve, address _user) external view returns (uint256) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; return user.lastVariableBorrowCumulativeIndex; } /** * @dev the variable borrow index of the user is 0 if the user is not borrowing or borrowing at stable * @param _reserve the address of the reserve for which the information is needed * @param _user the address of the user for which the information is needed * @return the variable borrow index for the user **/ function getUserLastUpdate(address _reserve, address _user) external view returns (uint256 timestamp) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; timestamp = user.lastUpdateTimestamp; } /** * @dev updates the lending pool core configuration **/ function refreshConfiguration() external onlyLendingPoolConfigurator { refreshConfigInternal(); } /** * @dev initializes a reserve * @param _reserve the address of the reserve * @param _aTokenAddress the address of the overlying aToken contract * @param _decimals the decimals of the reserve currency * @param _interestRateStrategyAddress the address of the interest rate strategy contract **/ function initReserve( address _reserve, address _aTokenAddress, uint256 _decimals, address _interestRateStrategyAddress ) external onlyLendingPoolConfigurator { reserves[_reserve].init(_aTokenAddress, _decimals, _interestRateStrategyAddress); addReserveToListInternal(_reserve); } /** * @dev removes the last added reserve in the reservesList array * @param _reserveToRemove the address of the reserve **/ function removeLastAddedReserve(address _reserveToRemove) external onlyLendingPoolConfigurator { address lastReserve = reservesList[reservesList.length-1]; require(lastReserve == _reserveToRemove, "Reserve being removed is different than the reserve requested"); //as we can't check if totalLiquidity is 0 (since the reserve added might not be an ERC20) we at least check that there is nothing borrowed require(getReserveTotalBorrows(lastReserve) == 0, "Cannot remove a reserve with liquidity deposited"); reserves[lastReserve].isActive = false; reserves[lastReserve].aTokenAddress = address(0); reserves[lastReserve].decimals = 0; reserves[lastReserve].lastLiquidityCumulativeIndex = 0; reserves[lastReserve].lastVariableBorrowCumulativeIndex = 0; reserves[lastReserve].borrowingEnabled = false; reserves[lastReserve].usageAsCollateralEnabled = false; reserves[lastReserve].baseLTVasCollateral = 0; reserves[lastReserve].liquidationThreshold = 0; reserves[lastReserve].liquidationBonus = 0; reserves[lastReserve].interestRateStrategyAddress = address(0); reservesList.pop(); } /** * @dev updates the address of the interest rate strategy contract * @param _reserve the address of the reserve * @param _rateStrategyAddress the address of the interest rate strategy contract **/ function setReserveInterestRateStrategyAddress(address _reserve, address _rateStrategyAddress) external onlyLendingPoolConfigurator { reserves[_reserve].interestRateStrategyAddress = _rateStrategyAddress; } /** * @dev enables borrowing on a reserve. Also sets the stable rate borrowing * @param _reserve the address of the reserve * @param _stableBorrowRateEnabled true if the stable rate needs to be enabled, false otherwise **/ function enableBorrowingOnReserve(address _reserve, bool _stableBorrowRateEnabled) external onlyLendingPoolConfigurator { reserves[_reserve].enableBorrowing(_stableBorrowRateEnabled); } /** * @dev disables borrowing on a reserve * @param _reserve the address of the reserve **/ function disableBorrowingOnReserve(address _reserve) external onlyLendingPoolConfigurator { reserves[_reserve].disableBorrowing(); } /** * @dev enables a reserve to be used as collateral * @param _reserve the address of the reserve **/ function enableReserveAsCollateral( address _reserve, uint256 _baseLTVasCollateral, uint256 _liquidationThreshold, uint256 _liquidationBonus ) external onlyLendingPoolConfigurator { reserves[_reserve].enableAsCollateral( _baseLTVasCollateral, _liquidationThreshold, _liquidationBonus ); } /** * @dev disables a reserve to be used as collateral * @param _reserve the address of the reserve **/ function disableReserveAsCollateral(address _reserve) external onlyLendingPoolConfigurator { reserves[_reserve].disableAsCollateral(); } /** * @dev enable the stable borrow rate mode on a reserve * @param _reserve the address of the reserve **/ function enableReserveStableBorrowRate(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isStableBorrowRateEnabled = true; } /** * @dev disable the stable borrow rate mode on a reserve * @param _reserve the address of the reserve **/ function disableReserveStableBorrowRate(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isStableBorrowRateEnabled = false; } /** * @dev activates a reserve * @param _reserve the address of the reserve **/ function activateReserve(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; require( reserve.lastLiquidityCumulativeIndex > 0 && reserve.lastVariableBorrowCumulativeIndex > 0, "Reserve has not been initialized yet" ); reserve.isActive = true; } /** * @dev deactivates a reserve * @param _reserve the address of the reserve **/ function deactivateReserve(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isActive = false; } /** * @notice allows the configurator to freeze the reserve. * A freezed reserve does not allow any action apart from repay, redeem, liquidationCall, rebalance. * @param _reserve the address of the reserve **/ function freezeReserve(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isFreezed = true; } /** * @notice allows the configurator to unfreeze the reserve. A unfreezed reserve allows any action to be executed. * @param _reserve the address of the reserve **/ function unfreezeReserve(address _reserve) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.isFreezed = false; } /** * @notice allows the configurator to update the loan to value of a reserve * @param _reserve the address of the reserve * @param _ltv the new loan to value **/ function setReserveBaseLTVasCollateral(address _reserve, uint256 _ltv) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.baseLTVasCollateral = _ltv; } /** * @notice allows the configurator to update the liquidation threshold of a reserve * @param _reserve the address of the reserve * @param _threshold the new liquidation threshold **/ function setReserveLiquidationThreshold(address _reserve, uint256 _threshold) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.liquidationThreshold = _threshold; } /** * @notice allows the configurator to update the liquidation bonus of a reserve * @param _reserve the address of the reserve * @param _bonus the new liquidation bonus **/ function setReserveLiquidationBonus(address _reserve, uint256 _bonus) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.liquidationBonus = _bonus; } /** * @notice allows the configurator to update the reserve decimals * @param _reserve the address of the reserve * @param _decimals the decimals of the reserve **/ function setReserveDecimals(address _reserve, uint256 _decimals) external onlyLendingPoolConfigurator { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; reserve.decimals = _decimals; } /** * @notice internal functions **/ /** * @dev updates the state of a reserve as a consequence of a borrow action. * @param _reserve the address of the reserve on which the user is borrowing * @param _user the address of the borrower * @param _principalBorrowBalance the previous borrow balance of the borrower before the action * @param _balanceIncrease the accrued interest of the user on the previous borrowed amount * @param _amountBorrowed the new amount borrowed * @param _rateMode the borrow rate mode (stable, variable) **/ function updateReserveStateOnBorrowInternal( address _reserve, address _user, uint256 _principalBorrowBalance, uint256 _balanceIncrease, uint256 _amountBorrowed, CoreLibrary.InterestRateMode _rateMode ) internal { reserves[_reserve].updateCumulativeIndexes(); //increasing reserve total borrows to account for the new borrow balance of the user //NOTE: Depending on the previous borrow mode, the borrows might need to be switched from variable to stable or vice versa updateReserveTotalBorrowsByRateModeInternal( _reserve, _user, _principalBorrowBalance, _balanceIncrease, _amountBorrowed, _rateMode ); } /** * @dev updates the state of a user as a consequence of a borrow action. * @param _reserve the address of the reserve on which the user is borrowing * @param _user the address of the borrower * @param _amountBorrowed the amount borrowed * @param _balanceIncrease the accrued interest of the user on the previous borrowed amount * @param _rateMode the borrow rate mode (stable, variable) * @return the final borrow rate for the user. Emitted by the borrow() event **/ function updateUserStateOnBorrowInternal( address _reserve, address _user, uint256 _amountBorrowed, uint256 _balanceIncrease, uint256 _fee, CoreLibrary.InterestRateMode _rateMode ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; if (_rateMode == CoreLibrary.InterestRateMode.STABLE) { //stable //reset the user variable index, and update the stable rate user.stableBorrowRate = reserve.currentStableBorrowRate; user.lastVariableBorrowCumulativeIndex = 0; } else if (_rateMode == CoreLibrary.InterestRateMode.VARIABLE) { //variable //reset the user stable rate, and store the new borrow index user.stableBorrowRate = 0; user.lastVariableBorrowCumulativeIndex = reserve.lastVariableBorrowCumulativeIndex; } else { revert("Invalid borrow rate mode"); } //increase the principal borrows and the origination fee user.principalBorrowBalance = user.principalBorrowBalance.add(_amountBorrowed).add( _balanceIncrease ); user.originationFee = user.originationFee.add(_fee); //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); } /** * @dev updates the state of the reserve as a consequence of a repay action. * @param _reserve the address of the reserve on which the user is repaying * @param _user the address of the borrower * @param _paybackAmountMinusFees the amount being paid back minus fees * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updateReserveStateOnRepayInternal( address _reserve, address _user, uint256 _paybackAmountMinusFees, uint256 _balanceIncrease ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_reserve][_user]; CoreLibrary.InterestRateMode borrowRateMode = getUserCurrentBorrowRateMode(_reserve, _user); //update the indexes reserves[_reserve].updateCumulativeIndexes(); //compound the cumulated interest to the borrow balance and then subtracting the payback amount if (borrowRateMode == CoreLibrary.InterestRateMode.STABLE) { reserve.increaseTotalBorrowsStableAndUpdateAverageRate( _balanceIncrease, user.stableBorrowRate ); reserve.decreaseTotalBorrowsStableAndUpdateAverageRate( _paybackAmountMinusFees, user.stableBorrowRate ); } else { reserve.increaseTotalBorrowsVariable(_balanceIncrease); reserve.decreaseTotalBorrowsVariable(_paybackAmountMinusFees); } } /** * @dev updates the state of the user as a consequence of a repay action. * @param _reserve the address of the reserve on which the user is repaying * @param _user the address of the borrower * @param _paybackAmountMinusFees the amount being paid back minus fees * @param _originationFeeRepaid the fee on the amount that is being repaid * @param _balanceIncrease the accrued interest on the borrowed amount * @param _repaidWholeLoan true if the user is repaying the whole loan **/ function updateUserStateOnRepayInternal( address _reserve, address _user, uint256 _paybackAmountMinusFees, uint256 _originationFeeRepaid, uint256 _balanceIncrease, bool _repaidWholeLoan ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; //update the user principal borrow balance, adding the cumulated interest and then subtracting the payback amount user.principalBorrowBalance = user.principalBorrowBalance.add(_balanceIncrease).sub( _paybackAmountMinusFees ); user.lastVariableBorrowCumulativeIndex = reserve.lastVariableBorrowCumulativeIndex; //if the balance decrease is equal to the previous principal (user is repaying the whole loan) //and the rate mode is stable, we reset the interest rate mode of the user if (_repaidWholeLoan) { user.stableBorrowRate = 0; user.lastVariableBorrowCumulativeIndex = 0; } user.originationFee = user.originationFee.sub(_originationFeeRepaid); //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); } /** * @dev updates the state of the user as a consequence of a swap rate action. * @param _reserve the address of the reserve on which the user is performing the rate swap * @param _user the address of the borrower * @param _principalBorrowBalance the the principal amount borrowed by the user * @param _compoundedBorrowBalance the principal amount plus the accrued interest * @param _currentRateMode the rate mode at which the user borrowed **/ function updateReserveStateOnSwapRateInternal( address _reserve, address _user, uint256 _principalBorrowBalance, uint256 _compoundedBorrowBalance, CoreLibrary.InterestRateMode _currentRateMode ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; //compounding reserve indexes reserve.updateCumulativeIndexes(); if (_currentRateMode == CoreLibrary.InterestRateMode.STABLE) { uint256 userCurrentStableRate = user.stableBorrowRate; //swap to variable reserve.decreaseTotalBorrowsStableAndUpdateAverageRate( _principalBorrowBalance, userCurrentStableRate ); //decreasing stable from old principal balance reserve.increaseTotalBorrowsVariable(_compoundedBorrowBalance); //increase variable borrows } else if (_currentRateMode == CoreLibrary.InterestRateMode.VARIABLE) { //swap to stable uint256 currentStableRate = reserve.currentStableBorrowRate; reserve.decreaseTotalBorrowsVariable(_principalBorrowBalance); reserve.increaseTotalBorrowsStableAndUpdateAverageRate( _compoundedBorrowBalance, currentStableRate ); } else { revert("Invalid rate mode received"); } } /** * @dev updates the state of the user as a consequence of a swap rate action. * @param _reserve the address of the reserve on which the user is performing the swap * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount * @param _currentRateMode the current rate mode of the user **/ function updateUserStateOnSwapRateInternal( address _reserve, address _user, uint256 _balanceIncrease, CoreLibrary.InterestRateMode _currentRateMode ) internal returns (CoreLibrary.InterestRateMode) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.InterestRateMode newMode = CoreLibrary.InterestRateMode.NONE; if (_currentRateMode == CoreLibrary.InterestRateMode.VARIABLE) { //switch to stable newMode = CoreLibrary.InterestRateMode.STABLE; user.stableBorrowRate = reserve.currentStableBorrowRate; user.lastVariableBorrowCumulativeIndex = 0; } else if (_currentRateMode == CoreLibrary.InterestRateMode.STABLE) { newMode = CoreLibrary.InterestRateMode.VARIABLE; user.stableBorrowRate = 0; user.lastVariableBorrowCumulativeIndex = reserve.lastVariableBorrowCumulativeIndex; } else { revert("Invalid interest rate mode received"); } //compounding cumulated interest user.principalBorrowBalance = user.principalBorrowBalance.add(_balanceIncrease); //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); return newMode; } /** * @dev updates the state of the principal reserve as a consequence of a liquidation action. * @param _principalReserve the address of the principal reserve that is being repaid * @param _user the address of the borrower * @param _amountToLiquidate the amount being repaid by the liquidator * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updatePrincipalReserveStateOnLiquidationInternal( address _principalReserve, address _user, uint256 _amountToLiquidate, uint256 _balanceIncrease ) internal { CoreLibrary.ReserveData storage reserve = reserves[_principalReserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_principalReserve]; //update principal reserve data reserve.updateCumulativeIndexes(); CoreLibrary.InterestRateMode borrowRateMode = getUserCurrentBorrowRateMode( _principalReserve, _user ); if (borrowRateMode == CoreLibrary.InterestRateMode.STABLE) { //increase the total borrows by the compounded interest reserve.increaseTotalBorrowsStableAndUpdateAverageRate( _balanceIncrease, user.stableBorrowRate ); //decrease by the actual amount to liquidate reserve.decreaseTotalBorrowsStableAndUpdateAverageRate( _amountToLiquidate, user.stableBorrowRate ); } else { //increase the total borrows by the compounded interest reserve.increaseTotalBorrowsVariable(_balanceIncrease); //decrease by the actual amount to liquidate reserve.decreaseTotalBorrowsVariable(_amountToLiquidate); } } /** * @dev updates the state of the collateral reserve as a consequence of a liquidation action. * @param _collateralReserve the address of the collateral reserve that is being liquidated **/ function updateCollateralReserveStateOnLiquidationInternal( address _collateralReserve ) internal { //update collateral reserve reserves[_collateralReserve].updateCumulativeIndexes(); } /** * @dev updates the state of the user being liquidated as a consequence of a liquidation action. * @param _reserve the address of the principal reserve that is being repaid * @param _user the address of the borrower * @param _amountToLiquidate the amount being repaid by the liquidator * @param _feeLiquidated the amount of origination fee being liquidated * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updateUserStateOnLiquidationInternal( address _reserve, address _user, uint256 _amountToLiquidate, uint256 _feeLiquidated, uint256 _balanceIncrease ) internal { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; CoreLibrary.ReserveData storage reserve = reserves[_reserve]; //first increase by the compounded interest, then decrease by the liquidated amount user.principalBorrowBalance = user.principalBorrowBalance.add(_balanceIncrease).sub( _amountToLiquidate ); if ( getUserCurrentBorrowRateMode(_reserve, _user) == CoreLibrary.InterestRateMode.VARIABLE ) { user.lastVariableBorrowCumulativeIndex = reserve.lastVariableBorrowCumulativeIndex; } if(_feeLiquidated > 0){ user.originationFee = user.originationFee.sub(_feeLiquidated); } //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); } /** * @dev updates the state of the reserve as a consequence of a stable rate rebalance * @param _reserve the address of the principal reserve where the user borrowed * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updateReserveStateOnRebalanceInternal( address _reserve, address _user, uint256 _balanceIncrease ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; reserve.updateCumulativeIndexes(); reserve.increaseTotalBorrowsStableAndUpdateAverageRate( _balanceIncrease, user.stableBorrowRate ); } /** * @dev updates the state of the user as a consequence of a stable rate rebalance * @param _reserve the address of the principal reserve where the user borrowed * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount **/ function updateUserStateOnRebalanceInternal( address _reserve, address _user, uint256 _balanceIncrease ) internal { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; CoreLibrary.ReserveData storage reserve = reserves[_reserve]; user.principalBorrowBalance = user.principalBorrowBalance.add(_balanceIncrease); user.stableBorrowRate = reserve.currentStableBorrowRate; //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); } /** * @dev updates the state of the user as a consequence of a stable rate rebalance * @param _reserve the address of the principal reserve where the user borrowed * @param _user the address of the borrower * @param _balanceIncrease the accrued interest on the borrowed amount * @param _amountBorrowed the accrued interest on the borrowed amount **/ function updateReserveTotalBorrowsByRateModeInternal( address _reserve, address _user, uint256 _principalBalance, uint256 _balanceIncrease, uint256 _amountBorrowed, CoreLibrary.InterestRateMode _newBorrowRateMode ) internal { CoreLibrary.InterestRateMode previousRateMode = getUserCurrentBorrowRateMode( _reserve, _user ); CoreLibrary.ReserveData storage reserve = reserves[_reserve]; if (previousRateMode == CoreLibrary.InterestRateMode.STABLE) { CoreLibrary.UserReserveData storage user = usersReserveData[_user][_reserve]; reserve.decreaseTotalBorrowsStableAndUpdateAverageRate( _principalBalance, user.stableBorrowRate ); } else if (previousRateMode == CoreLibrary.InterestRateMode.VARIABLE) { reserve.decreaseTotalBorrowsVariable(_principalBalance); } uint256 newPrincipalAmount = _principalBalance.add(_balanceIncrease).add(_amountBorrowed); if (_newBorrowRateMode == CoreLibrary.InterestRateMode.STABLE) { reserve.increaseTotalBorrowsStableAndUpdateAverageRate( newPrincipalAmount, reserve.currentStableBorrowRate ); } else if (_newBorrowRateMode == CoreLibrary.InterestRateMode.VARIABLE) { reserve.increaseTotalBorrowsVariable(newPrincipalAmount); } else { revert("Invalid new borrow rate mode"); } } /** * @dev Updates the reserve current stable borrow rate Rf, the current variable borrow rate Rv and the current liquidity rate Rl. * Also updates the lastUpdateTimestamp value. Please refer to the whitepaper for further information. * @param _reserve the address of the reserve to be updated * @param _liquidityAdded the amount of liquidity added to the protocol (deposit or repay) in the previous action * @param _liquidityTaken the amount of liquidity taken from the protocol (redeem or borrow) **/ function updateReserveInterestRatesAndTimestampInternal( address _reserve, uint256 _liquidityAdded, uint256 _liquidityTaken ) internal { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; (uint256 newLiquidityRate, uint256 newStableRate, uint256 newVariableRate) = IReserveInterestRateStrategy( reserve .interestRateStrategyAddress ) .calculateInterestRates( _reserve, getReserveAvailableLiquidity(_reserve).add(_liquidityAdded).sub(_liquidityTaken), reserve.totalBorrowsStable, reserve.totalBorrowsVariable, reserve.currentAverageStableBorrowRate ); reserve.currentLiquidityRate = newLiquidityRate; reserve.currentStableBorrowRate = newStableRate; reserve.currentVariableBorrowRate = newVariableRate; //solium-disable-next-line reserve.lastUpdateTimestamp = uint40(block.timestamp); emit ReserveUpdated( _reserve, newLiquidityRate, newStableRate, newVariableRate, reserve.lastLiquidityCumulativeIndex, reserve.lastVariableBorrowCumulativeIndex ); } /** * @dev transfers to the protocol fees of a flashloan to the fees collection address * @param _token the address of the token being transferred * @param _amount the amount being transferred **/ function transferFlashLoanProtocolFeeInternal(address _token, uint256 _amount) internal { address payable receiver = address(uint160(addressesProvider.getTokenDistributor())); if (_token != EthAddressLib.ethAddress()) { ERC20(_token).safeTransfer(receiver, _amount); } else { receiver.transfer(_amount); } } /** * @dev updates the internal configuration of the core **/ function refreshConfigInternal() internal { lendingPoolAddress = addressesProvider.getLendingPool(); } /** * @dev adds a reserve to the array of the reserves address **/ function addReserveToListInternal(address _reserve) internal { bool reserveAlreadyAdded = false; for (uint256 i = 0; i < reservesList.length; i++) if (reservesList[i] == _reserve) { reserveAlreadyAdded = true; } if (!reserveAlreadyAdded) reservesList.push(_reserve); } } /** * @title LendingPool contract * @notice Implements the actions of the LendingPool, and exposes accessory methods to fetch the users and reserve data * @author Aave **/ contract LendingPool is ReentrancyGuard, VersionedInitializable { using SafeMath for uint256; using WadRayMath for uint256; using Address for address; LendingPoolAddressesProvider public addressesProvider; LendingPoolCore public core; LendingPoolDataProvider public dataProvider; LendingPoolParametersProvider public parametersProvider; IFeeProvider feeProvider; /** * @dev emitted on deposit * @param _reserve the address of the reserve * @param _user the address of the user * @param _amount the amount to be deposited * @param _referral the referral number of the action * @param _timestamp the timestamp of the action **/ event Deposit( address indexed _reserve, address indexed _user, uint256 _amount, uint16 indexed _referral, uint256 _timestamp ); /** * @dev emitted during a redeem action. * @param _reserve the address of the reserve * @param _user the address of the user * @param _amount the amount to be deposited * @param _timestamp the timestamp of the action **/ event RedeemUnderlying( address indexed _reserve, address indexed _user, uint256 _amount, uint256 _timestamp ); /** * @dev emitted on borrow * @param _reserve the address of the reserve * @param _user the address of the user * @param _amount the amount to be deposited * @param _borrowRateMode the rate mode, can be either 1-stable or 2-variable * @param _borrowRate the rate at which the user has borrowed * @param _originationFee the origination fee to be paid by the user * @param _borrowBalanceIncrease the balance increase since the last borrow, 0 if it's the first time borrowing * @param _referral the referral number of the action * @param _timestamp the timestamp of the action **/ event Borrow( address indexed _reserve, address indexed _user, uint256 _amount, uint256 _borrowRateMode, uint256 _borrowRate, uint256 _originationFee, uint256 _borrowBalanceIncrease, uint16 indexed _referral, uint256 _timestamp ); /** * @dev emitted on repay * @param _reserve the address of the reserve * @param _user the address of the user for which the repay has been executed * @param _repayer the address of the user that has performed the repay action * @param _amountMinusFees the amount repaid minus fees * @param _fees the fees repaid * @param _borrowBalanceIncrease the balance increase since the last action * @param _timestamp the timestamp of the action **/ event Repay( address indexed _reserve, address indexed _user, address indexed _repayer, uint256 _amountMinusFees, uint256 _fees, uint256 _borrowBalanceIncrease, uint256 _timestamp ); /** * @dev emitted when a user performs a rate swap * @param _reserve the address of the reserve * @param _user the address of the user executing the swap * @param _newRateMode the new interest rate mode * @param _newRate the new borrow rate * @param _borrowBalanceIncrease the balance increase since the last action * @param _timestamp the timestamp of the action **/ event Swap( address indexed _reserve, address indexed _user, uint256 _newRateMode, uint256 _newRate, uint256 _borrowBalanceIncrease, uint256 _timestamp ); /** * @dev emitted when a user enables a reserve as collateral * @param _reserve the address of the reserve * @param _user the address of the user **/ event ReserveUsedAsCollateralEnabled(address indexed _reserve, address indexed _user); /** * @dev emitted when a user disables a reserve as collateral * @param _reserve the address of the reserve * @param _user the address of the user **/ event ReserveUsedAsCollateralDisabled(address indexed _reserve, address indexed _user); /** * @dev emitted when the stable rate of a user gets rebalanced * @param _reserve the address of the reserve * @param _user the address of the user for which the rebalance has been executed * @param _newStableRate the new stable borrow rate after the rebalance * @param _borrowBalanceIncrease the balance increase since the last action * @param _timestamp the timestamp of the action **/ event RebalanceStableBorrowRate( address indexed _reserve, address indexed _user, uint256 _newStableRate, uint256 _borrowBalanceIncrease, uint256 _timestamp ); /** * @dev emitted when a flashloan is executed * @param _target the address of the flashLoanReceiver * @param _reserve the address of the reserve * @param _amount the amount requested * @param _totalFee the total fee on the amount * @param _protocolFee the part of the fee for the protocol * @param _timestamp the timestamp of the action **/ event FlashLoan( address indexed _target, address indexed _reserve, uint256 _amount, uint256 _totalFee, uint256 _protocolFee, uint256 _timestamp ); /** * @dev these events are not emitted directly by the LendingPool * but they are declared here as the LendingPoolLiquidationManager * is executed using a delegateCall(). * This allows to have the events in the generated ABI for LendingPool. **/ /** * @dev emitted when a borrow fee is liquidated * @param _collateral the address of the collateral being liquidated * @param _reserve the address of the reserve * @param _user the address of the user being liquidated * @param _feeLiquidated the total fee liquidated * @param _liquidatedCollateralForFee the amount of collateral received by the protocol in exchange for the fee * @param _timestamp the timestamp of the action **/ event OriginationFeeLiquidated( address indexed _collateral, address indexed _reserve, address indexed _user, uint256 _feeLiquidated, uint256 _liquidatedCollateralForFee, uint256 _timestamp ); /** * @dev emitted when a borrower is liquidated * @param _collateral the address of the collateral being liquidated * @param _reserve the address of the reserve * @param _user the address of the user being liquidated * @param _purchaseAmount the total amount liquidated * @param _liquidatedCollateralAmount the amount of collateral being liquidated * @param _accruedBorrowInterest the amount of interest accrued by the borrower since the last action * @param _liquidator the address of the liquidator * @param _receiveAToken true if the liquidator wants to receive aTokens, false otherwise * @param _timestamp the timestamp of the action **/ event LiquidationCall( address indexed _collateral, address indexed _reserve, address indexed _user, uint256 _purchaseAmount, uint256 _liquidatedCollateralAmount, uint256 _accruedBorrowInterest, address _liquidator, bool _receiveAToken, uint256 _timestamp ); /** * @dev functions affected by this modifier can only be invoked by the * aToken.sol contract * @param _reserve the address of the reserve **/ modifier onlyOverlyingAToken(address _reserve) { require( msg.sender == core.getReserveATokenAddress(_reserve), "The caller of this function can only be the aToken contract of this reserve" ); _; } /** * @dev functions affected by this modifier can only be invoked if the reserve is active * @param _reserve the address of the reserve **/ modifier onlyActiveReserve(address _reserve) { requireReserveActiveInternal(_reserve); _; } /** * @dev functions affected by this modifier can only be invoked if the reserve is not freezed. * A freezed reserve only allows redeems, repays, rebalances and liquidations. * @param _reserve the address of the reserve **/ modifier onlyUnfreezedReserve(address _reserve) { requireReserveNotFreezedInternal(_reserve); _; } /** * @dev functions affected by this modifier can only be invoked if the provided _amount input parameter * is not zero. * @param _amount the amount provided **/ modifier onlyAmountGreaterThanZero(uint256 _amount) { requireAmountGreaterThanZeroInternal(_amount); _; } uint256 public constant UINT_MAX_VALUE = uint256(-1); uint256 public constant LENDINGPOOL_REVISION = 0x2; function getRevision() internal pure returns (uint256) { return LENDINGPOOL_REVISION; } /** * @dev this function is invoked by the proxy contract when the LendingPool contract is added to the * AddressesProvider. * @param _addressesProvider the address of the LendingPoolAddressesProvider registry **/ function initialize(LendingPoolAddressesProvider _addressesProvider) public initializer { addressesProvider = _addressesProvider; core = LendingPoolCore(addressesProvider.getLendingPoolCore()); dataProvider = LendingPoolDataProvider(addressesProvider.getLendingPoolDataProvider()); parametersProvider = LendingPoolParametersProvider( addressesProvider.getLendingPoolParametersProvider() ); feeProvider = IFeeProvider(addressesProvider.getFeeProvider()); } /** * @dev deposits The underlying asset into the reserve. A corresponding amount of the overlying asset (aTokens) * is minted. * @param _reserve the address of the reserve * @param _amount the amount to be deposited * @param _referralCode integrators are assigned a referral code and can potentially receive rewards. **/ function deposit(address _reserve, uint256 _amount, uint16 _referralCode) external payable nonReentrant onlyActiveReserve(_reserve) onlyUnfreezedReserve(_reserve) onlyAmountGreaterThanZero(_amount) { AToken aToken = AToken(core.getReserveATokenAddress(_reserve)); bool isFirstDeposit = aToken.balanceOf(msg.sender) == 0; core.updateStateOnDeposit(_reserve, msg.sender, _amount, isFirstDeposit); //minting AToken to user 1:1 with the specific exchange rate aToken.mintOnDeposit(msg.sender, _amount); //transfer to the core contract core.transferToReserve.value(msg.value)(_reserve, msg.sender, _amount); //solium-disable-next-line emit Deposit(_reserve, msg.sender, _amount, _referralCode, block.timestamp); } /** * @dev Redeems the underlying amount of assets requested by _user. * This function is executed by the overlying aToken contract in response to a redeem action. * @param _reserve the address of the reserve * @param _user the address of the user performing the action * @param _amount the underlying amount to be redeemed **/ function redeemUnderlying( address _reserve, address payable _user, uint256 _amount, uint256 _aTokenBalanceAfterRedeem ) external nonReentrant onlyOverlyingAToken(_reserve) onlyActiveReserve(_reserve) onlyAmountGreaterThanZero(_amount) { uint256 currentAvailableLiquidity = core.getReserveAvailableLiquidity(_reserve); require( currentAvailableLiquidity >= _amount, "There is not enough liquidity available to redeem" ); core.updateStateOnRedeem(_reserve, _user, _amount, _aTokenBalanceAfterRedeem == 0); core.transferToUser(_reserve, _user, _amount); //solium-disable-next-line emit RedeemUnderlying(_reserve, _user, _amount, block.timestamp); } /** * @dev data structures for local computations in the borrow() method. */ struct BorrowLocalVars { uint256 principalBorrowBalance; uint256 currentLtv; uint256 currentLiquidationThreshold; uint256 borrowFee; uint256 requestedBorrowAmountETH; uint256 amountOfCollateralNeededETH; uint256 userCollateralBalanceETH; uint256 userBorrowBalanceETH; uint256 userTotalFeesETH; uint256 borrowBalanceIncrease; uint256 currentReserveStableRate; uint256 availableLiquidity; uint256 reserveDecimals; uint256 finalUserBorrowRate; CoreLibrary.InterestRateMode rateMode; bool healthFactorBelowThreshold; } /** * @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower * already deposited enough collateral. * @param _reserve the address of the reserve * @param _amount the amount to be borrowed * @param _interestRateMode the interest rate mode at which the user wants to borrow. Can be 0 (STABLE) or 1 (VARIABLE) **/ function borrow( address _reserve, uint256 _amount, uint256 _interestRateMode, uint16 _referralCode ) external nonReentrant onlyActiveReserve(_reserve) onlyUnfreezedReserve(_reserve) onlyAmountGreaterThanZero(_amount) { // Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables BorrowLocalVars memory vars; //check that the reserve is enabled for borrowing require(core.isReserveBorrowingEnabled(_reserve), "Reserve is not enabled for borrowing"); //validate interest rate mode require( uint256(CoreLibrary.InterestRateMode.VARIABLE) == _interestRateMode || uint256(CoreLibrary.InterestRateMode.STABLE) == _interestRateMode, "Invalid interest rate mode selected" ); //cast the rateMode to coreLibrary.interestRateMode vars.rateMode = CoreLibrary.InterestRateMode(_interestRateMode); //check that the amount is available in the reserve vars.availableLiquidity = core.getReserveAvailableLiquidity(_reserve); require( vars.availableLiquidity >= _amount, "There is not enough liquidity available in the reserve" ); ( , vars.userCollateralBalanceETH, vars.userBorrowBalanceETH, vars.userTotalFeesETH, vars.currentLtv, vars.currentLiquidationThreshold, , vars.healthFactorBelowThreshold ) = dataProvider.calculateUserGlobalData(msg.sender); require(vars.userCollateralBalanceETH > 0, "The collateral balance is 0"); require( !vars.healthFactorBelowThreshold, "The borrower can already be liquidated so he cannot borrow more" ); //calculating fees vars.borrowFee = feeProvider.calculateLoanOriginationFee(msg.sender, _amount); require(vars.borrowFee > 0, "The amount to borrow is too small"); vars.amountOfCollateralNeededETH = dataProvider.calculateCollateralNeededInETH( _reserve, _amount, vars.borrowFee, vars.userBorrowBalanceETH, vars.userTotalFeesETH, vars.currentLtv ); require( vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH, "There is not enough collateral to cover a new borrow" ); /** * Following conditions need to be met if the user is borrowing at a stable rate: * 1. Reserve must be enabled for stable rate borrowing * 2. Users cannot borrow from the reserve if their collateral is (mostly) the same currency * they are borrowing, to prevent abuses. * 3. Users will be able to borrow only a relatively small, configurable amount of the total * liquidity **/ if (vars.rateMode == CoreLibrary.InterestRateMode.STABLE) { //check if the borrow mode is stable and if stable rate borrowing is enabled on this reserve require( core.isUserAllowedToBorrowAtStable(_reserve, msg.sender, _amount), "User cannot borrow the selected amount with a stable rate" ); //calculate the max available loan size in stable rate mode as a percentage of the //available liquidity uint256 maxLoanPercent = parametersProvider.getMaxStableRateBorrowSizePercent(); uint256 maxLoanSizeStable = vars.availableLiquidity.mul(maxLoanPercent).div(100); require( _amount <= maxLoanSizeStable, "User is trying to borrow too much liquidity at a stable rate" ); } //all conditions passed - borrow is accepted (vars.finalUserBorrowRate, vars.borrowBalanceIncrease) = core.updateStateOnBorrow( _reserve, msg.sender, _amount, vars.borrowFee, vars.rateMode ); //if we reached this point, we can transfer core.transferToUser(_reserve, msg.sender, _amount); emit Borrow( _reserve, msg.sender, _amount, _interestRateMode, vars.finalUserBorrowRate, vars.borrowFee, vars.borrowBalanceIncrease, _referralCode, //solium-disable-next-line block.timestamp ); } /** * @notice repays a borrow on the specific reserve, for the specified amount (or for the whole amount, if uint256(-1) is specified). * @dev the target user is defined by _onBehalfOf. If there is no repayment on behalf of another account, * _onBehalfOf must be equal to msg.sender. * @param _reserve the address of the reserve on which the user borrowed * @param _amount the amount to repay, or uint256(-1) if the user wants to repay everything * @param _onBehalfOf the address for which msg.sender is repaying. **/ struct RepayLocalVars { uint256 principalBorrowBalance; uint256 compoundedBorrowBalance; uint256 borrowBalanceIncrease; bool isETH; uint256 paybackAmount; uint256 paybackAmountMinusFees; uint256 currentStableRate; uint256 originationFee; } function repay(address _reserve, uint256 _amount, address payable _onBehalfOf) external payable nonReentrant onlyActiveReserve(_reserve) onlyAmountGreaterThanZero(_amount) { // Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables RepayLocalVars memory vars; ( vars.principalBorrowBalance, vars.compoundedBorrowBalance, vars.borrowBalanceIncrease ) = core.getUserBorrowBalances(_reserve, _onBehalfOf); vars.originationFee = core.getUserOriginationFee(_reserve, _onBehalfOf); vars.isETH = EthAddressLib.ethAddress() == _reserve; require(vars.compoundedBorrowBalance > 0, "The user does not have any borrow pending"); require( _amount != UINT_MAX_VALUE || msg.sender == _onBehalfOf, "To repay on behalf of an user an explicit amount to repay is needed." ); //default to max amount vars.paybackAmount = vars.compoundedBorrowBalance.add(vars.originationFee); if (_amount != UINT_MAX_VALUE && _amount < vars.paybackAmount) { vars.paybackAmount = _amount; } require( !vars.isETH || msg.value >= vars.paybackAmount, "Invalid msg.value sent for the repayment" ); //if the amount is smaller than the origination fee, just transfer the amount to the fee destination address if (vars.paybackAmount <= vars.originationFee) { core.updateStateOnRepay( _reserve, _onBehalfOf, 0, vars.paybackAmount, vars.borrowBalanceIncrease, false ); core.transferToFeeCollectionAddress.value(vars.isETH ? vars.paybackAmount : 0)( _reserve, _onBehalfOf, vars.paybackAmount, addressesProvider.getTokenDistributor() ); emit Repay( _reserve, _onBehalfOf, msg.sender, 0, vars.paybackAmount, vars.borrowBalanceIncrease, //solium-disable-next-line block.timestamp ); return; } vars.paybackAmountMinusFees = vars.paybackAmount.sub(vars.originationFee); core.updateStateOnRepay( _reserve, _onBehalfOf, vars.paybackAmountMinusFees, vars.originationFee, vars.borrowBalanceIncrease, vars.compoundedBorrowBalance == vars.paybackAmountMinusFees ); //if the user didn't repay the origination fee, transfer the fee to the fee collection address if(vars.originationFee > 0) { core.transferToFeeCollectionAddress.value(vars.isETH ? vars.originationFee : 0)( _reserve, _onBehalfOf, vars.originationFee, addressesProvider.getTokenDistributor() ); } //sending the total msg.value if the transfer is ETH. //the transferToReserve() function will take care of sending the //excess ETH back to the caller core.transferToReserve.value(vars.isETH ? msg.value.sub(vars.originationFee) : 0)( _reserve, msg.sender, vars.paybackAmountMinusFees ); emit Repay( _reserve, _onBehalfOf, msg.sender, vars.paybackAmountMinusFees, vars.originationFee, vars.borrowBalanceIncrease, //solium-disable-next-line block.timestamp ); } /** * @dev borrowers can user this function to swap between stable and variable borrow rate modes. * @param _reserve the address of the reserve on which the user borrowed **/ function swapBorrowRateMode(address _reserve) external nonReentrant onlyActiveReserve(_reserve) onlyUnfreezedReserve(_reserve) { (uint256 principalBorrowBalance, uint256 compoundedBorrowBalance, uint256 borrowBalanceIncrease) = core .getUserBorrowBalances(_reserve, msg.sender); require( compoundedBorrowBalance > 0, "User does not have a borrow in progress on this reserve" ); CoreLibrary.InterestRateMode currentRateMode = core.getUserCurrentBorrowRateMode( _reserve, msg.sender ); if (currentRateMode == CoreLibrary.InterestRateMode.VARIABLE) { /** * user wants to swap to stable, before swapping we need to ensure that * 1. stable borrow rate is enabled on the reserve * 2. user is not trying to abuse the reserve by depositing * more collateral than he is borrowing, artificially lowering * the interest rate, borrowing at variable, and switching to stable **/ require( core.isUserAllowedToBorrowAtStable(_reserve, msg.sender, compoundedBorrowBalance), "User cannot borrow the selected amount at stable" ); } (CoreLibrary.InterestRateMode newRateMode, uint256 newBorrowRate) = core .updateStateOnSwapRate( _reserve, msg.sender, principalBorrowBalance, compoundedBorrowBalance, borrowBalanceIncrease, currentRateMode ); emit Swap( _reserve, msg.sender, uint256(newRateMode), newBorrowRate, borrowBalanceIncrease, //solium-disable-next-line block.timestamp ); } /** * @dev rebalances the stable interest rate of a user if current liquidity rate > user stable rate. * this is regulated by Aave to ensure that the protocol is not abused, and the user is paying a fair * rate. Anyone can call this function though. * @param _reserve the address of the reserve * @param _user the address of the user to be rebalanced **/ function rebalanceStableBorrowRate(address _reserve, address _user) external nonReentrant onlyActiveReserve(_reserve) { (, uint256 compoundedBalance, uint256 borrowBalanceIncrease) = core.getUserBorrowBalances( _reserve, _user ); //step 1: user must be borrowing on _reserve at a stable rate require(compoundedBalance > 0, "User does not have any borrow for this reserve"); require( core.getUserCurrentBorrowRateMode(_reserve, _user) == CoreLibrary.InterestRateMode.STABLE, "The user borrow is variable and cannot be rebalanced" ); uint256 userCurrentStableRate = core.getUserCurrentStableBorrowRate(_reserve, _user); uint256 liquidityRate = core.getReserveCurrentLiquidityRate(_reserve); uint256 reserveCurrentStableRate = core.getReserveCurrentStableBorrowRate(_reserve); uint256 rebalanceDownRateThreshold = reserveCurrentStableRate.rayMul( WadRayMath.ray().add(parametersProvider.getRebalanceDownRateDelta()) ); //step 2: we have two possible situations to rebalance: //1. user stable borrow rate is below the current liquidity rate. The loan needs to be rebalanced, //as this situation can be abused (user putting back the borrowed liquidity in the same reserve to earn on it) //2. user stable rate is above the market avg borrow rate of a certain delta, and utilization rate is low. //In this case, the user is paying an interest that is too high, and needs to be rescaled down. if ( userCurrentStableRate < liquidityRate || userCurrentStableRate > rebalanceDownRateThreshold ) { uint256 newStableRate = core.updateStateOnRebalance( _reserve, _user, borrowBalanceIncrease ); emit RebalanceStableBorrowRate( _reserve, _user, newStableRate, borrowBalanceIncrease, //solium-disable-next-line block.timestamp ); return; } revert("Interest rate rebalance conditions were not met"); } /** * @dev allows depositors to enable or disable a specific deposit as collateral. * @param _reserve the address of the reserve * @param _useAsCollateral true if the user wants to user the deposit as collateral, false otherwise. **/ function setUserUseReserveAsCollateral(address _reserve, bool _useAsCollateral) external nonReentrant onlyActiveReserve(_reserve) onlyUnfreezedReserve(_reserve) { uint256 underlyingBalance = core.getUserUnderlyingAssetBalance(_reserve, msg.sender); require(underlyingBalance > 0, "User does not have any liquidity deposited"); require( dataProvider.balanceDecreaseAllowed(_reserve, msg.sender, underlyingBalance), "User deposit is already being used as collateral" ); core.setUserUseReserveAsCollateral(_reserve, msg.sender, _useAsCollateral); if (_useAsCollateral) { emit ReserveUsedAsCollateralEnabled(_reserve, msg.sender); } else { emit ReserveUsedAsCollateralDisabled(_reserve, msg.sender); } } /** * @dev users can invoke this function to liquidate an undercollateralized position. * @param _reserve the address of the collateral to liquidated * @param _reserve the address of the principal reserve * @param _user the address of the borrower * @param _purchaseAmount the amount of principal that the liquidator wants to repay * @param _receiveAToken true if the liquidators wants to receive the aTokens, false if * he wants to receive the underlying asset directly **/ function liquidationCall( address _collateral, address _reserve, address _user, uint256 _purchaseAmount, bool _receiveAToken ) external payable nonReentrant onlyActiveReserve(_reserve) onlyActiveReserve(_collateral) { address liquidationManager = addressesProvider.getLendingPoolLiquidationManager(); //solium-disable-next-line (bool success, bytes memory result) = liquidationManager.delegatecall( abi.encodeWithSignature( "liquidationCall(address,address,address,uint256,bool)", _collateral, _reserve, _user, _purchaseAmount, _receiveAToken ) ); require(success, "Liquidation call failed"); (uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string)); if (returnCode != 0) { //error found revert(string(abi.encodePacked("Liquidation failed: ", returnMessage))); } } /** * @dev allows smartcontracts to access the liquidity of the pool within one transaction, * as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts * that must be kept into consideration. For further details please visit https://developers.aave.com * @param _receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface. * @param _reserve the address of the principal reserve * @param _amount the amount requested for this flashloan **/ function flashLoan(address _receiver, address _reserve, uint256 _amount, bytes memory _params) public nonReentrant onlyActiveReserve(_reserve) onlyAmountGreaterThanZero(_amount) { //check that the reserve has enough available liquidity //we avoid using the getAvailableLiquidity() function in LendingPoolCore to save gas uint256 availableLiquidityBefore = _reserve == EthAddressLib.ethAddress() ? address(core).balance : IERC20(_reserve).balanceOf(address(core)); require( availableLiquidityBefore >= _amount, "There is not enough liquidity available to borrow" ); (uint256 totalFeeBips, uint256 protocolFeeBips) = parametersProvider .getFlashLoanFeesInBips(); //calculate amount fee uint256 amountFee = _amount.mul(totalFeeBips).div(10000); //protocol fee is the part of the amountFee reserved for the protocol - the rest goes to depositors uint256 protocolFee = amountFee.mul(protocolFeeBips).div(10000); require( amountFee > 0 && protocolFee > 0, "The requested amount is too small for a flashLoan." ); //get the FlashLoanReceiver instance IFlashLoanReceiver receiver = IFlashLoanReceiver(_receiver); address payable userPayable = address(uint160(_receiver)); //transfer funds to the receiver core.transferToUser(_reserve, userPayable, _amount); //execute action of the receiver receiver.executeOperation(_reserve, _amount, amountFee, _params); //check that the actual balance of the core contract includes the returned amount uint256 availableLiquidityAfter = _reserve == EthAddressLib.ethAddress() ? address(core).balance : IERC20(_reserve).balanceOf(address(core)); require( availableLiquidityAfter == availableLiquidityBefore.add(amountFee), "The actual balance of the protocol is inconsistent" ); core.updateStateOnFlashLoan( _reserve, availableLiquidityBefore, amountFee.sub(protocolFee), protocolFee ); //solium-disable-next-line emit FlashLoan(_receiver, _reserve, _amount, amountFee, protocolFee, block.timestamp); } /** * @dev accessory functions to fetch data from the core contract **/ function getReserveConfigurationData(address _reserve) external view returns ( uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus, address interestRateStrategyAddress, bool usageAsCollateralEnabled, bool borrowingEnabled, bool stableBorrowRateEnabled, bool isActive ) { return dataProvider.getReserveConfigurationData(_reserve); } function getReserveData(address _reserve) external view returns ( uint256 totalLiquidity, uint256 availableLiquidity, uint256 totalBorrowsStable, uint256 totalBorrowsVariable, uint256 liquidityRate, uint256 variableBorrowRate, uint256 stableBorrowRate, uint256 averageStableBorrowRate, uint256 utilizationRate, uint256 liquidityIndex, uint256 variableBorrowIndex, address aTokenAddress, uint40 lastUpdateTimestamp ) { return dataProvider.getReserveData(_reserve); } function getUserAccountData(address _user) external view returns ( uint256 totalLiquidityETH, uint256 totalCollateralETH, uint256 totalBorrowsETH, uint256 totalFeesETH, uint256 availableBorrowsETH, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor ) { return dataProvider.getUserAccountData(_user); } function getUserReserveData(address _reserve, address _user) external view returns ( uint256 currentATokenBalance, uint256 currentBorrowBalance, uint256 principalBorrowBalance, uint256 borrowRateMode, uint256 borrowRate, uint256 liquidityRate, uint256 originationFee, uint256 variableBorrowIndex, uint256 lastUpdateTimestamp, bool usageAsCollateralEnabled ) { return dataProvider.getUserReserveData(_reserve, _user); } function getReserves() external view returns (address[] memory) { return core.getReserves(); } /** * @dev internal function to save on code size for the onlyActiveReserve modifier **/ function requireReserveActiveInternal(address _reserve) internal view { require(core.getReserveIsActive(_reserve), "Action requires an active reserve"); } /** * @notice internal function to save on code size for the onlyUnfreezedReserve modifier **/ function requireReserveNotFreezedInternal(address _reserve) internal view { require(!core.getReserveIsFreezed(_reserve), "Action requires an unfreezed reserve"); } /** * @notice internal function to save on code size for the onlyAmountGreaterThanZero modifier **/ function requireAmountGreaterThanZeroInternal(uint256 _amount) internal pure { require(_amount > 0, "Amount must be greater than 0"); } } /** * @title LendingPoolLiquidationManager contract * @author Aave * @notice Implements the liquidation function. **/ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializable { using SafeMath for uint256; using WadRayMath for uint256; using Address for address; LendingPoolAddressesProvider public addressesProvider; LendingPoolCore core; LendingPoolDataProvider dataProvider; LendingPoolParametersProvider parametersProvider; IFeeProvider feeProvider; address ethereumAddress; uint256 constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 50; /** * @dev emitted when a borrow fee is liquidated * @param _collateral the address of the collateral being liquidated * @param _reserve the address of the reserve * @param _user the address of the user being liquidated * @param _feeLiquidated the total fee liquidated * @param _liquidatedCollateralForFee the amount of collateral received by the protocol in exchange for the fee * @param _timestamp the timestamp of the action **/ event OriginationFeeLiquidated( address indexed _collateral, address indexed _reserve, address indexed _user, uint256 _feeLiquidated, uint256 _liquidatedCollateralForFee, uint256 _timestamp ); /** * @dev emitted when a borrower is liquidated * @param _collateral the address of the collateral being liquidated * @param _reserve the address of the reserve * @param _user the address of the user being liquidated * @param _purchaseAmount the total amount liquidated * @param _liquidatedCollateralAmount the amount of collateral being liquidated * @param _accruedBorrowInterest the amount of interest accrued by the borrower since the last action * @param _liquidator the address of the liquidator * @param _receiveAToken true if the liquidator wants to receive aTokens, false otherwise * @param _timestamp the timestamp of the action **/ event LiquidationCall( address indexed _collateral, address indexed _reserve, address indexed _user, uint256 _purchaseAmount, uint256 _liquidatedCollateralAmount, uint256 _accruedBorrowInterest, address _liquidator, bool _receiveAToken, uint256 _timestamp ); enum LiquidationErrors { NO_ERROR, NO_COLLATERAL_AVAILABLE, COLLATERAL_CANNOT_BE_LIQUIDATED, CURRRENCY_NOT_BORROWED, HEALTH_FACTOR_ABOVE_THRESHOLD, NOT_ENOUGH_LIQUIDITY } struct LiquidationCallLocalVars { uint256 userCollateralBalance; uint256 userCompoundedBorrowBalance; uint256 borrowBalanceIncrease; uint256 maxPrincipalAmountToLiquidate; uint256 actualAmountToLiquidate; uint256 liquidationRatio; uint256 collateralPrice; uint256 principalCurrencyPrice; uint256 maxAmountCollateralToLiquidate; uint256 originationFee; uint256 feeLiquidated; uint256 liquidatedCollateralForFee; CoreLibrary.InterestRateMode borrowRateMode; uint256 userStableRate; bool isCollateralEnabled; bool healthFactorBelowThreshold; } /** * @dev as the contract extends the VersionedInitializable contract to match the state * of the LendingPool contract, the getRevision() function is needed. */ function getRevision() internal pure returns (uint256) { return 0; } /** * @dev users can invoke this function to liquidate an undercollateralized position. * @param _reserve the address of the collateral to liquidated * @param _reserve the address of the principal reserve * @param _user the address of the borrower * @param _purchaseAmount the amount of principal that the liquidator wants to repay * @param _receiveAToken true if the liquidators wants to receive the aTokens, false if * he wants to receive the underlying asset directly **/ function liquidationCall( address _collateral, address _reserve, address _user, uint256 _purchaseAmount, bool _receiveAToken ) external payable returns (uint256, string memory) { // Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables LiquidationCallLocalVars memory vars; (, , , , , , , vars.healthFactorBelowThreshold) = dataProvider.calculateUserGlobalData( _user ); if (!vars.healthFactorBelowThreshold) { return ( uint256(LiquidationErrors.HEALTH_FACTOR_ABOVE_THRESHOLD), "Health factor is not below the threshold" ); } vars.userCollateralBalance = core.getUserUnderlyingAssetBalance(_collateral, _user); //if _user hasn't deposited this specific collateral, nothing can be liquidated if (vars.userCollateralBalance == 0) { return ( uint256(LiquidationErrors.NO_COLLATERAL_AVAILABLE), "Invalid collateral to liquidate" ); } vars.isCollateralEnabled = core.isReserveUsageAsCollateralEnabled(_collateral) && core.isUserUseReserveAsCollateralEnabled(_collateral, _user); //if _collateral isn't enabled as collateral by _user, it cannot be liquidated if (!vars.isCollateralEnabled) { return ( uint256(LiquidationErrors.COLLATERAL_CANNOT_BE_LIQUIDATED), "The collateral chosen cannot be liquidated" ); } //if the user hasn't borrowed the specific currency defined by _reserve, it cannot be liquidated (, vars.userCompoundedBorrowBalance, vars.borrowBalanceIncrease) = core .getUserBorrowBalances(_reserve, _user); if (vars.userCompoundedBorrowBalance == 0) { return ( uint256(LiquidationErrors.CURRRENCY_NOT_BORROWED), "User did not borrow the specified currency" ); } //all clear - calculate the max principal amount that can be liquidated vars.maxPrincipalAmountToLiquidate = vars .userCompoundedBorrowBalance .mul(LIQUIDATION_CLOSE_FACTOR_PERCENT) .div(100); vars.actualAmountToLiquidate = _purchaseAmount > vars.maxPrincipalAmountToLiquidate ? vars.maxPrincipalAmountToLiquidate : _purchaseAmount; (uint256 maxCollateralToLiquidate, uint256 principalAmountNeeded) = calculateAvailableCollateralToLiquidate( _collateral, _reserve, vars.actualAmountToLiquidate, vars.userCollateralBalance ); vars.originationFee = core.getUserOriginationFee(_reserve, _user); //if there is a fee to liquidate, calculate the maximum amount of fee that can be liquidated if (vars.originationFee > 0) { ( vars.liquidatedCollateralForFee, vars.feeLiquidated ) = calculateAvailableCollateralToLiquidate( _collateral, _reserve, vars.originationFee, vars.userCollateralBalance.sub(maxCollateralToLiquidate) ); } //if principalAmountNeeded < vars.ActualAmountToLiquidate, there isn't enough //of _collateral to cover the actual amount that is being liquidated, hence we liquidate //a smaller amount if (principalAmountNeeded < vars.actualAmountToLiquidate) { vars.actualAmountToLiquidate = principalAmountNeeded; } //if liquidator reclaims the underlying asset, we make sure there is enough available collateral in the reserve if (!_receiveAToken) { uint256 currentAvailableCollateral = core.getReserveAvailableLiquidity(_collateral); if (currentAvailableCollateral < maxCollateralToLiquidate) { return ( uint256(LiquidationErrors.NOT_ENOUGH_LIQUIDITY), "There isn't enough liquidity available to liquidate" ); } } core.updateStateOnLiquidation( _reserve, _collateral, _user, vars.actualAmountToLiquidate, maxCollateralToLiquidate, vars.feeLiquidated, vars.liquidatedCollateralForFee, vars.borrowBalanceIncrease, _receiveAToken ); AToken collateralAtoken = AToken(core.getReserveATokenAddress(_collateral)); //if liquidator reclaims the aToken, he receives the equivalent atoken amount if (_receiveAToken) { collateralAtoken.transferOnLiquidation(_user, msg.sender, maxCollateralToLiquidate); } else { //otherwise receives the underlying asset //burn the equivalent amount of atoken collateralAtoken.burnOnLiquidation(_user, maxCollateralToLiquidate); core.transferToUser(_collateral, msg.sender, maxCollateralToLiquidate); } //transfers the principal currency to the pool core.transferToReserve.value(msg.value)(_reserve, msg.sender, vars.actualAmountToLiquidate); if (vars.feeLiquidated > 0) { //if there is enough collateral to liquidate the fee, first transfer burn an equivalent amount of //aTokens of the user collateralAtoken.burnOnLiquidation(_user, vars.liquidatedCollateralForFee); //then liquidate the fee by transferring it to the fee collection address core.liquidateFee( _collateral, vars.liquidatedCollateralForFee, addressesProvider.getTokenDistributor() ); emit OriginationFeeLiquidated( _collateral, _reserve, _user, vars.feeLiquidated, vars.liquidatedCollateralForFee, //solium-disable-next-line block.timestamp ); } emit LiquidationCall( _collateral, _reserve, _user, vars.actualAmountToLiquidate, maxCollateralToLiquidate, vars.borrowBalanceIncrease, msg.sender, _receiveAToken, //solium-disable-next-line block.timestamp ); return (uint256(LiquidationErrors.NO_ERROR), "No errors"); } struct AvailableCollateralToLiquidateLocalVars { uint256 userCompoundedBorrowBalance; uint256 liquidationBonus; uint256 collateralPrice; uint256 principalCurrencyPrice; uint256 maxAmountCollateralToLiquidate; } /** * @dev calculates how much of a specific collateral can be liquidated, given * a certain amount of principal currency. This function needs to be called after * all the checks to validate the liquidation have been performed, otherwise it might fail. * @param _collateral the collateral to be liquidated * @param _principal the principal currency to be liquidated * @param _purchaseAmount the amount of principal being liquidated * @param _userCollateralBalance the collatera balance for the specific _collateral asset of the user being liquidated * @return the maximum amount that is possible to liquidated given all the liquidation constraints (user balance, close factor) and * the purchase amount **/ function calculateAvailableCollateralToLiquidate( address _collateral, address _principal, uint256 _purchaseAmount, uint256 _userCollateralBalance ) internal view returns (uint256 collateralAmount, uint256 principalAmountNeeded) { collateralAmount = 0; principalAmountNeeded = 0; IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle()); // Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables AvailableCollateralToLiquidateLocalVars memory vars; vars.collateralPrice = oracle.getAssetPrice(_collateral); vars.principalCurrencyPrice = oracle.getAssetPrice(_principal); vars.liquidationBonus = core.getReserveLiquidationBonus(_collateral); //this is the maximum possible amount of the selected collateral that can be liquidated, given the //max amount of principal currency that is available for liquidation. vars.maxAmountCollateralToLiquidate = vars .principalCurrencyPrice .mul(_purchaseAmount) .div(vars.collateralPrice) .mul(vars.liquidationBonus) .div(100); if (vars.maxAmountCollateralToLiquidate > _userCollateralBalance) { collateralAmount = _userCollateralBalance; principalAmountNeeded = vars .collateralPrice .mul(collateralAmount) .div(vars.principalCurrencyPrice) .mul(100) .div(vars.liquidationBonus); } else { collateralAmount = vars.maxAmountCollateralToLiquidate; principalAmountNeeded = _purchaseAmount; } return (collateralAmount, principalAmountNeeded); } }
File 11 of 11: IdleAave
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol pragma solidity ^0.5.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see {ERC20Detailed}. */ 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); } // File: @openzeppelin/contracts/token/ERC20/ERC20Detailed.sol pragma solidity ^0.5.0; /** * @dev Optional functions from the ERC20 standard. */ contract ERC20Detailed is IERC20 { string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of * these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol, uint8 decimals) public { _name = name; _symbol = symbol; _decimals = decimals; } /** * @dev Returns the name of the token. */ function name() public 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 _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. * * 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 _decimals; } } // File: @openzeppelin/contracts/math/SafeMath.sol pragma solidity ^0.5.0; /** * @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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ 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. * * _Available since v2.4.0._ */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File: @openzeppelin/contracts/utils/Address.sol pragma solidity ^0.5.5; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing 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. */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. // 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 != 0x0 && codehash != accountHash); } /** * @dev Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * _Available since v2.4.0._ */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } /** * @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]. * * _Available since v2.4.0._ */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-call-value (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol pragma solidity ^0.5.0; /** * @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 ERC20;` 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 { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length 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 safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length 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"); } } } // File: @openzeppelin/contracts/GSN/Context.sol pragma solidity ^0.5.0; /* * @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. */ contract Context { // Empty internal constructor, to prevent people from mistakenly deploying // an instance of this contract, which should be used via inheritance. constructor () internal { } // solhint-disable-previous-line no-empty-blocks function _msgSender() internal view returns (address payable) { return msg.sender; } function _msgData() internal view returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File: @openzeppelin/contracts/ownership/Ownable.sol pragma solidity ^0.5.0; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { _owner = _msgSender(); emit OwnershipTransferred(address(0), _owner); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } /** * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { return _msgSender() == _owner; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/interfaces/ILendingProtocol.sol pragma solidity 0.5.16; interface ILendingProtocol { function mint() external returns (uint256); function redeem(address account) external returns (uint256); function nextSupplyRate(uint256 amount) external view returns (uint256); function nextSupplyRateWithParams(uint256[] calldata params) external view returns (uint256); function getAPR() external view returns (uint256); function getPriceInToken() external view returns (uint256); function token() external view returns (address); function underlying() external view returns (address); function availableLiquidity() external view returns (uint256); } // File: contracts/interfaces/AaveLendingPoolProvider.sol pragma solidity 0.5.16; interface AaveLendingPoolProvider { function getLendingPool() external view returns (address); function getLendingPoolCore() external view returns (address); } // File: contracts/interfaces/AaveLendingPool.sol pragma solidity 0.5.16; interface AaveLendingPool { function deposit(address _reserve, uint256 _amount, uint16 _referralCode) external; function getReserveData(address _reserve) external view returns ( uint256 totalLiquidity, uint256 availableLiquidity, uint256 totalBorrowsStable, uint256 totalBorrowsVariable, uint256 liquidityRate, uint256 variableBorrowRate, uint256 stableBorrowRate, uint256 averageStableBorrowRate, uint256 utilizationRate, uint256 liquidityIndex, uint256 variableBorrowIndex, address aTokenAddress, uint40 lastUpdateTimestamp ); } // File: contracts/interfaces/AaveLendingPoolCore.sol pragma solidity 0.5.16; interface AaveLendingPoolCore { function getReserveCurrentLiquidityRate(address _reserve) external view returns (uint256); function getReserveInterestRateStrategyAddress(address _reserve) external view returns (address); function getReserveTotalBorrowsStable(address _reserve) external view returns (uint256); function getReserveTotalBorrowsVariable(address _reserve) external view returns (uint256); function getReserveCurrentAverageStableBorrowRate(address _reserve) external view returns (uint256); function getReserveAvailableLiquidity(address _reserve) external view returns (uint256); } // File: contracts/interfaces/AToken.sol pragma solidity 0.5.16; interface AToken { function redeem(uint256 amount) external; } // File: contracts/interfaces/AaveInterestRateStrategy.sol pragma solidity 0.5.16; interface AaveInterestRateStrategy { function getBaseVariableBorrowRate() external view returns (uint256); function calculateInterestRates( address _reserve, uint256 _utilizationRate, uint256 _totalBorrowsStable, uint256 _totalBorrowsVariable, uint256 _averageStableBorrowRate) external view returns (uint256 liquidityRate, uint256 stableBorrowRate, uint256 variableBorrowRate); } // File: contracts/wrappers/IdleAave.sol /** * @title: Aave wrapper * @summary: Used for interacting with Aave. Has * a common interface with all other protocol wrappers. * This contract holds assets only during a tx, after tx it should be empty * @author: Idle Labs Inc., idle.finance */ pragma solidity 0.5.16; contract IdleAave is ILendingProtocol, Ownable { using SafeERC20 for IERC20; using SafeMath for uint256; // protocol token (aToken) address address public token; // underlying token (token eg DAI) address address public underlying; address public idleToken; address public constant aaveAddressesProvider = address(0x24a42fD28C976A61Df5D00D0599C34c4f90748c8); AaveLendingPoolProvider provider = AaveLendingPoolProvider(aaveAddressesProvider); /** * @param _token : aToken address * @param _underlying : underlying token (eg DAI) address */ constructor(address _token, address _underlying) public { require(_token != address(0) && _underlying != address(0), 'AAVE: some addr is 0'); token = _token; underlying = _underlying; IERC20(_underlying).safeApprove( provider.getLendingPoolCore(), uint256(-1) ); } /** * Throws if called by any account other than IdleToken contract. */ modifier onlyIdle() { require(msg.sender == idleToken, "Ownable: caller is not IdleToken"); _; } // onlyOwner /** * sets idleToken address * NOTE: can be called only once. It's not on the constructor because we are deploying this contract * after the IdleToken contract * @param _idleToken : idleToken address */ function setIdleToken(address _idleToken) external onlyOwner { require(idleToken == address(0), "idleToken addr already set"); require(_idleToken != address(0), "_idleToken addr is 0"); idleToken = _idleToken; } // end onlyOwner /** * Calculate next supply rate for Aave, given an `_amount` supplied (last array param) * and all other params supplied. * on calculations. * * @param params : array with all params needed for calculation (see below) * @return : yearly net rate */ function nextSupplyRateWithParams(uint256[] calldata params) external view returns (uint256) { AaveLendingPoolCore core = AaveLendingPoolCore(provider.getLendingPoolCore()); AaveInterestRateStrategy apr = AaveInterestRateStrategy(core.getReserveInterestRateStrategyAddress(underlying)); /* params[0] = core.getReserveAvailableLiquidity(underlying); params[1] = core.getReserveTotalBorrowsStable(underlying); params[2] = core.getReserveTotalBorrowsVariable(underlying); params[3] = core.getReserveCurrentAverageStableBorrowRate(underlying); params[4] = _amount; */ (uint256 newLiquidityRate,,) = apr.calculateInterestRates( underlying, params[0].add(params[4]), params[1], params[2], params[3] ); // newLiquidityRate is in RAY (ie 1e27) // also newLiquidityRate is in the form 0.03 * 1e27 // while we need the result in the form 3 * 1e18 return newLiquidityRate.mul(100).div(10**9); } /** * Calculate next supply rate for Aave, given an `_amount` supplied * * @param _amount : new underlying amount supplied (eg DAI) * @return : yearly net rate */ function nextSupplyRate(uint256 _amount) external view returns (uint256) { AaveLendingPoolCore core = AaveLendingPoolCore(provider.getLendingPoolCore()); AaveInterestRateStrategy apr = AaveInterestRateStrategy(core.getReserveInterestRateStrategyAddress(underlying)); (uint256 newLiquidityRate,,) = apr.calculateInterestRates( underlying, core.getReserveAvailableLiquidity(underlying).add(_amount), core.getReserveTotalBorrowsStable(underlying), core.getReserveTotalBorrowsVariable(underlying), core.getReserveCurrentAverageStableBorrowRate(underlying) ); return newLiquidityRate.mul(100).div(10**9); } /** * @return current price of aToken in underlying, Aave price is always 1 */ function getPriceInToken() external view returns (uint256) { return 10**18; } /** * @return apr : current yearly net rate */ function getAPR() external view returns (uint256) { AaveLendingPoolCore core = AaveLendingPoolCore(provider.getLendingPoolCore()); return core.getReserveCurrentLiquidityRate(underlying).mul(100).div(10**9); } /** * Gets all underlying tokens in this contract and mints aTokens * tokens are then transferred to msg.sender * NOTE: underlying tokens needs to be sent here before calling this * * @return aTokens minted */ function mint() external onlyIdle returns (uint256 aTokens) { uint256 balance = IERC20(underlying).balanceOf(address(this)); if (balance == 0) { return aTokens; } AaveLendingPool lendingPool = AaveLendingPool(provider.getLendingPool()); lendingPool.deposit(underlying, balance, 29); // 29 -> referral aTokens = IERC20(token).balanceOf(address(this)); // transfer them to the caller IERC20(token).safeTransfer(msg.sender, aTokens); } /** * Gets all aTokens in this contract and redeems underlying tokens. * underlying tokens are then transferred to `_account` * NOTE: aTokens needs to be sent here before calling this * * @return underlying tokens redeemd */ function redeem(address _account) external onlyIdle returns (uint256 tokens) { AToken(token).redeem(IERC20(token).balanceOf(address(this))); IERC20 _underlying = IERC20(underlying); tokens = _underlying.balanceOf(address(this)); _underlying.safeTransfer(_account, tokens); } function availableLiquidity() external view returns (uint256) { AaveLendingPoolCore core = AaveLendingPoolCore(provider.getLendingPoolCore()); return IERC20(underlying).balanceOf(address(core)); } }