Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Transfer Ownersh... | 13442470 | 1214 days ago | IN | 0 ETH | 0.00331739 |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
InvoicePool
Compiler Version
v0.5.16+commit.9c3226ce
Contract Source Code (Solidity Multiple files format)
pragma solidity ^0.5.0; import "./SafeMath.sol"; import "./Ownable.sol"; import "./Address.sol"; import "./IERC20.sol"; import "./VersionedInitializable.sol"; import "./MintableERC20.sol"; import "./MockUSDC.sol"; import "./LendingPoolAddressesProvider.sol"; // import "../configuration/LendingPoolParametersProvider.sol"; import "./PToken.sol"; // import "../libraries/WadRayMath.sol"; import "./IFeeProvider.sol"; import "./IPriceOracleGetter.sol"; import "./LendingPool.sol"; import "./LendingPoolDataProvider.sol"; contract InvoicePool is Ownable { using SafeMath for uint256; using Address for address; /// FIELD/GLOBAL VARIABLES uint256 public constant UINT_MAX_VALUE = uint256(-1); LendingPool public lendingPool; address public lendingPoolCore; LendingPoolDataProvider public lendingPoolDataProvider; LendingPoolAddressesProvider public lendingPoolAddressesProvider; address public PAX; MockUSDC public USDp; /// EVENTS event CollateralDeposited(address indexed token, uint256 amount); event CollateralWithdrawn(address indexed token, uint256 amount); event Borrowed(address indexed token, uint256 amount); event Repaid(address indexed token, uint256 amount); modifier onlyWalletOrOwner() { require(msg.sender == address(this) || msg.sender == owner(), "caller should be contract itself or owner"); _; } constructor( address _USDpToken, address _PAXToken, address _lendingPool, address _lendingPoolCore ) public { require( (_USDpToken != address(0) && _lendingPool != address(0) && _PAXToken != address(0) && _lendingPoolCore != address(0)), "Invalid zero address passed as constructor argument" ); USDp = MockUSDC(_USDpToken); PAX = _PAXToken; lendingPool = LendingPool(_lendingPool); lendingPoolCore = _lendingPoolCore; lendingPoolAddressesProvider = LendingPoolAddressesProvider(address(lendingPool.addressesProvider)); lendingPoolDataProvider = LendingPoolDataProvider(address(lendingPool.dataProvider)); } function deposit(uint256 amount) public onlyWalletOrOwner { // if balance of contract is less than amount, mint tokens to reach balance if (USDp.balanceOf(address(this)) < amount) { USDp.mint(amount.sub(USDp.balanceOf(address(this)))); } // approve lending pool core before deposit USDp.approve(lendingPoolCore, amount); // deposit amount of tokens into lending pool // initial deposit is automatically set as collateral lendingPool.deposit(address(USDp), amount, 0); // emit deposit event emit CollateralDeposited(address(USDp), amount); } function withdraw() public onlyWalletOrOwner { address PTokenAddress; ( , , , , , , , , , , , PTokenAddress, ) = lendingPool.getReserveData(address(USDp)); uint256 previousBalance = USDp.balanceOf(address(this)); // will only redeem tokens not used/locked as collateral from lending pool PToken(PTokenAddress).redeem(UINT_MAX_VALUE); uint256 currentBalance = USDp.balanceOf(address(this)); uint256 withdrawnAmount = currentBalance.sub(previousBalance); USDp.burn(withdrawnAmount); emit CollateralWithdrawn(address(USDp), withdrawnAmount); } function borrow(uint256 amount) external onlyOwner { uint256 borrowFee = IFeeProvider(lendingPoolAddressesProvider.getFeeProvider()).calculateLoanOriginationFee(address(this), amount); ( , uint256 totalCollateralBalanceETH, uint256 totalBorrowBalanceETH, uint256 totalFeesETH, uint256 currentLtv, , , ) = lendingPoolDataProvider.calculateUserGlobalData(address(this)); uint256 amountOfCollateralNeededETH = lendingPoolDataProvider.calculateCollateralNeededInETH( PAX, amount, borrowFee, totalBorrowBalanceETH, totalFeesETH, currentLtv ); if (amountOfCollateralNeededETH > totalCollateralBalanceETH) { IPriceOracleGetter oracle = IPriceOracleGetter( lendingPoolAddressesProvider.getPriceOracle() ); uint256 tokenUnit = 10**USDp.decimals(); uint256 reserveUnitPrice = oracle.getAssetPrice(address(USDp)); uint256 collateralToDeposit = amountOfCollateralNeededETH.mul(reserveUnitPrice).div( tokenUnit); USDp.approve(lendingPoolCore, collateralToDeposit); if (USDp.balanceOf(address(this)) < collateralToDeposit) { USDp.mint(collateralToDeposit.sub(USDp.balanceOf(address(this)))); } deposit(collateralToDeposit); } lendingPool.borrow(PAX, amount, 1, 0); emit Borrowed(PAX, amount); } function repay(uint256 amount) external onlyOwner { address payable invoicePool = address(uint160(address(this))); IERC20(PAX).approve(lendingPoolCore, amount); lendingPool.repay(PAX, amount, invoicePool); // after repay, compute how much USDp collateral can be withdrawn in PToken contract and // burn when received withdraw(); emit Repaid(PAX, amount); } }
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; 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; } }
pragma solidity ^0.5.0; import "./UpgradeabilityProxy.sol"; /** * @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(); } }
pragma solidity ^0.5.0; import "./Proxy.sol"; import "./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; //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) } } }
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; } }
pragma solidity ^0.5.0; import "./SafeMath.sol"; import "./WadRayMath.sol"; /** * CoreLibrary library * - * Defines the data structures of the reserves and the user data * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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 PToken representing the asset **/ address PTokenAddress; /** * @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 _PTokenAddress the address of the overlying PToken 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 _PTokenAddress, uint256 _decimals, address _interestRateStrategyAddress ) external { require( _self.PTokenAddress == 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.PTokenAddress = _PTokenAddress; _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); } }
pragma solidity ^0.5.0; import "./IReserveInterestRateStrategy.sol"; import "./WadRayMath.sol"; import "./LendingPoolAddressesProvider.sol"; import "./LendingPoolCore.sol"; import "./ILendingRateOracle.sol"; import "./SafeMath.sol"; /** * DefaultReserveInterestRateStrategy contract * - * implements the calculation of the interest rates depending on the reserve parameters. * if there is need to update the calculation of the interest rates for a specific reserve, * a new version of this contract will be deployed. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { using WadRayMath for uint256; using SafeMath for uint256; /** * @dev this constant represents the utilization rate at which the pool aims to obtain most competitive borrow rates * expressed in ray **/ uint256 public constant OPTIMAL_UTILIZATION_RATE = 0.8 * 1e27; /** * @dev this constant represents the excess utilization rate above the optimal. It's always equal to * 1-optimal utilization rate. Added as a constant here for gas optimizations * expressed in ray **/ uint256 public constant EXCESS_UTILIZATION_RATE = 0.2 * 1e27; LendingPoolAddressesProvider public addressesProvider; //base variable borrow rate when Utilization rate = 0. Expressed in ray uint256 public baseVariableBorrowRate; //slope of the variable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray uint256 public variableRateSlope1; //slope of the variable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray uint256 public variableRateSlope2; //slope of the stable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray uint256 public stableRateSlope1; //slope of the stable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray uint256 public stableRateSlope2; address public reserve; constructor( address _reserve, LendingPoolAddressesProvider _provider, uint256 _baseVariableBorrowRate, uint256 _variableRateSlope1, uint256 _variableRateSlope2, uint256 _stableRateSlope1, uint256 _stableRateSlope2 ) public { addressesProvider = _provider; baseVariableBorrowRate = _baseVariableBorrowRate; variableRateSlope1 = _variableRateSlope1; variableRateSlope2 = _variableRateSlope2; stableRateSlope1 = _stableRateSlope1; stableRateSlope2 = _stableRateSlope2; reserve = _reserve; } /** @dev accessors */ function getBaseVariableBorrowRate() external view returns (uint256) { return baseVariableBorrowRate; } function getVariableRateSlope1() external view returns (uint256) { return variableRateSlope1; } function getVariableRateSlope2() external view returns (uint256) { return variableRateSlope2; } function getStableRateSlope1() external view returns (uint256) { return stableRateSlope1; } function getStableRateSlope2() external view returns (uint256) { return stableRateSlope2; } /** * @dev calculates the interest rates depending on the available liquidity and the total borrowed. * @param _reserve the address of the reserve * @param _availableLiquidity the liquidity available in the reserve * @param _totalBorrowsStable the total borrowed from the reserve a stable rate * @param _totalBorrowsVariable the total borrowed from the reserve at a variable rate * @param _averageStableBorrowRate the weighted average of all the stable rate borrows * @return the liquidity rate, stable borrow rate and variable borrow rate calculated from the input parameters **/ function calculateInterestRates( address _reserve, uint256 _availableLiquidity, uint256 _totalBorrowsStable, uint256 _totalBorrowsVariable, uint256 _averageStableBorrowRate ) external view returns ( uint256 currentLiquidityRate, uint256 currentStableBorrowRate, uint256 currentVariableBorrowRate ) { uint256 totalBorrows = _totalBorrowsStable.add(_totalBorrowsVariable); uint256 utilizationRate = (totalBorrows == 0 && _availableLiquidity == 0) ? 0 : totalBorrows.rayDiv(_availableLiquidity.add(totalBorrows)); currentStableBorrowRate = ILendingRateOracle(addressesProvider.getLendingRateOracle()) .getMarketBorrowRate(_reserve); if (utilizationRate > OPTIMAL_UTILIZATION_RATE) { uint256 excessUtilizationRateRatio = utilizationRate .sub(OPTIMAL_UTILIZATION_RATE) .rayDiv(EXCESS_UTILIZATION_RATE); currentStableBorrowRate = currentStableBorrowRate.add(stableRateSlope1).add( stableRateSlope2.rayMul(excessUtilizationRateRatio) ); currentVariableBorrowRate = baseVariableBorrowRate.add(variableRateSlope1).add( variableRateSlope2.rayMul(excessUtilizationRateRatio) ); } else { currentStableBorrowRate = currentStableBorrowRate.add( stableRateSlope1.rayMul( utilizationRate.rayDiv( OPTIMAL_UTILIZATION_RATE ) ) ); currentVariableBorrowRate = baseVariableBorrowRate.add( utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE).rayMul(variableRateSlope1) ); } currentLiquidityRate = getOverallBorrowRateInternal( _totalBorrowsStable, _totalBorrowsVariable, currentVariableBorrowRate, _averageStableBorrowRate ) .rayMul(utilizationRate); } /** * @dev calculates the overall borrow rate as the weighted average between the total variable borrows and total stable borrows. * @param _totalBorrowsStable the total borrowed from the reserve a stable rate * @param _totalBorrowsVariable the total borrowed from the reserve at a variable rate * @param _currentVariableBorrowRate the current variable borrow rate * @param _currentAverageStableBorrowRate the weighted average of all the stable rate borrows * @return the weighted averaged borrow rate **/ function getOverallBorrowRateInternal( uint256 _totalBorrowsStable, uint256 _totalBorrowsVariable, uint256 _currentVariableBorrowRate, uint256 _currentAverageStableBorrowRate ) internal pure returns (uint256) { uint256 totalBorrows = _totalBorrowsStable.add(_totalBorrowsVariable); if (totalBorrows == 0) return 0; uint256 weightedVariableRate = _totalBorrowsVariable.wadToRay().rayMul( _currentVariableBorrowRate ); uint256 weightedStableRate = _totalBorrowsStable.wadToRay().rayMul( _currentAverageStableBorrowRate ); uint256 overallBorrowRate = weightedVariableRate.add(weightedStableRate).rayDiv( totalBorrows.wadToRay() ); return overallBorrowRate; } }
pragma solidity ^0.5.0; import "./Context.sol"; import "./IERC20.sol"; import "./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 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")); } }
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; 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; } }
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; /** * IFeeProvider interface * - * Interface for the Populous fee provider. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ interface IFeeProvider { function calculateLoanOriginationFee(address _user, uint256 _amount) external view returns (uint256); function getLoanOriginationFeePercentage() external view returns (uint256); }
pragma solidity ^0.5.0; /** * IFlashLoanReceiver interface * - * implement this interface to develop a flashloan-compatible flashLoanReceiver contract * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ interface IFlashLoanReceiver { function executeOperation(address _reserve, uint256 _amount, uint256 _fee, bytes calldata _params) external; }
pragma solidity ^0.5.0; /** * ILendingPoolAddressesProvider interface * - * Provides the interface to fetch the LendingPoolCore address * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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; }
pragma solidity ^0.5.0; /** * ILendingRateOracle interface * - * Interface for the Populous borrow rate oracle. Provides the average market borrow rate to be used as a base for the stable borrow rate calculations * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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; }
pragma solidity ^0.5.0; import "./BaseAdminUpgradeabilityProxy.sol"; import "./InitializableUpgradeabilityProxy.sol"; /** * @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); } }
pragma solidity ^0.5.0; import "./BaseUpgradeabilityProxy.sol"; /** * @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); } } }
pragma solidity ^0.5.0; /************ @title IPriceOracleGetter interface @notice */ /** * IPriceOracle interface * - * Interface for the Populous price oracle. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ interface IPriceOracleGetter { /*********** @dev returns the asset price in ETH */ function getAssetPrice(address _asset) external view returns (uint256); }
pragma solidity ^0.5.0; //note create a proper PToken interface interface IPToken { event Redeem( address indexed _from, uint256 _value, uint256 _fromBalanceIncrease, uint256 _fromIndex ); event MintOnDeposit( address indexed _from, uint256 _value, uint256 _fromBalanceIncrease, uint256 _fromIndex ); event BurnOnLiquidation( address indexed _from, uint256 _value, uint256 _fromBalanceIncrease, uint256 _fromIndex ); event BalanceTransfer( address indexed _from, address indexed _to, uint256 _value, uint256 _fromBalanceIncrease, uint256 _toBalanceIncrease, uint256 _fromIndex, uint256 _toIndex ); event InterestStreamRedirected( address indexed _from, address indexed _to, uint256 _redirectedBalance, uint256 _fromBalanceIncrease, uint256 _fromIndex ); event RedirectedBalanceUpdated( address indexed _targetAddress, uint256 _targetBalanceIncrease, uint256 _targetIndex, uint256 _redirectedBalanceAdded, uint256 _redirectedBalanceRemoved ); event InterestRedirectionAllowanceChanged( address indexed _from, address indexed _to ); function balanceOf(address _user) external view returns (uint256); }
pragma solidity ^0.5.0; /** * IReserveInterestRateStrategyInterface interface * - * Interface for the calculation of the interest rates. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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); }
pragma solidity ^0.5.0; import "./SafeMath.sol"; import "./ReentrancyGuard.sol"; import "./Address.sol"; import "./IERC20.sol"; import "./VersionedInitializable.sol"; import "./LendingPoolAddressesProvider.sol"; import "./LendingPoolParametersProvider.sol"; import "./PToken.sol"; import "./CoreLibrary.sol"; import "./WadRayMath.sol"; import "./IFeeProvider.sol"; import "./IFlashLoanReceiver.sol"; import "./LendingPoolCore.sol"; import "./LendingPoolDataProvider.sol"; import "./LendingPoolLiquidationManager.sol"; import "./EthAddressLib.sol"; import "./LendingPoolConfigurator.sol"; import "./DefaultReserveInterestRateStrategy.sol"; /** * LendingPool contract * - * Implements the actions of the LendingPool, and exposes accessory methods to fetch the users and reserve data * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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 _receivePToken true if the liquidator wants to receive PTokens, 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 _receivePToken, uint256 _timestamp ); /** * @dev check that reserve borrowed from is not enabled for invoice pool * @param _reserve the address of the reserve **/ modifier isNotInvoiceLendingPool(address _reserve){ LendingPoolConfigurator poolConfig = LendingPoolConfigurator( addressesProvider.getLendingPoolConfigurator() ); require( poolConfig.isReserveEnabledForInvoicePool(_reserve) != true, "reserve is an invoice lending pool reserve, cannot swap borrow rate from stable" ); _; } /** * @dev functions affected by this modifier can only be invoked by the * PToken.sol contract * @param _reserve the address of the reserve **/ modifier onlyOverlyingPToken(address _reserve) { require( msg.sender == core.getReservePTokenAddress(_reserve), "The caller of this function can only be the PToken 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 = 0x5; 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 (PTokens) * 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) { PToken pToken = PToken(core.getReservePTokenAddress(_reserve)); bool isFirstDeposit = pToken.balanceOf(msg.sender) == 0; core.updateStateOnDeposit(_reserve, msg.sender, _amount, isFirstDeposit); //minting PToken to user 1:1 with the specific exchange rate pToken.mintOnDeposit(msg.sender, _amount); //transfer deposit from senders address 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 PToken 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 _PTokenBalanceAfterRedeem ) external //nonReentrant onlyOverlyingPToken(_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, _PTokenBalanceAfterRedeem == 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; } //invoice lending pool checks function calculateUserReserveCollateralETHInvoicePool( address _reserveToBorrowFrom, uint256 _amount, uint256 _interestRateMode ) public view returns (uint256, uint256) { //TODO: change to internal or modifier // Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables BorrowLocalVars memory vars; LendingPoolConfigurator poolConfig = LendingPoolConfigurator( addressesProvider.getLendingPoolConfigurator() ); //if condition is true, then run this whole function internally or as modifier //check if reserve to borrow from is enabled for invoice pool if (poolConfig.isReserveEnabledForInvoicePool(_reserveToBorrowFrom) != true ) { //check reserve address is set in configurator return (0, 0); //_; } address interestRateStrategyAddress; ( , , , interestRateStrategyAddress, , , , ) = dataProvider.getReserveConfigurationData(_reserveToBorrowFrom); DefaultReserveInterestRateStrategy irs = DefaultReserveInterestRateStrategy(interestRateStrategyAddress); require((irs.getStableRateSlope1() != 0) || (irs.getStableRateSlope2() != 0), "no stable interest rates set for reserve to borrow from"); //check that reserve to borrow from has stable interest rates set in interest rate strategy contract require(_interestRateMode == uint256(CoreLibrary.InterestRateMode.STABLE), "only stable rate mode allowed when borrowing from invoice lending pool reserves"); //get users total collateral balance in ETH for invoice lending pool collateral reserves/tokens vars.borrowFee = feeProvider.calculateLoanOriginationFee(msg.sender, _amount); //the fee to pay based on the amount to borrow //calculate total collateral balance in ETH for a user based on their balances of ALL reserves that have been set as invoice lending pool collateral reserves e.g., GBP using calculateUserInvoiceCollateralETH function in LendingPoolDataProvider vars.userCollateralBalanceETH = dataProvider.calculateUserInvoiceCollateralETH(msg.sender); ( , , vars.userBorrowBalanceETH, vars.userTotalFeesETH, vars.currentLtv, vars.currentLiquidationThreshold, , vars.healthFactorBelowThreshold ) = dataProvider.calculateUserGlobalData(msg.sender); vars.amountOfCollateralNeededETH = dataProvider.calculateCollateralNeededInETH( //checking collateral amount needed based on the reserve in collateral _reserveToBorrowFrom, _amount, vars.borrowFee, vars.userBorrowBalanceETH, vars.userTotalFeesETH, vars.currentLtv ); require(vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH, "There is not enough collateral to cover a new invoice lending pool stable borrow"); return (vars.userCollateralBalanceETH, vars.amountOfCollateralNeededETH); //_; } /** * @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 1 (STABLE) or 2 (VARIABLE) **/ function borrow( address _reserve, uint256 _amount, uint256 _interestRateMode, uint16 _referralCode ) external //nonReentrant onlyActiveReserve(_reserve) onlyUnfreezedReserve(_reserve) onlyAmountGreaterThanZero(_amount) { //checks _reserve to borrow from is invoice lending pool reserve set in lending pool configurator by lending pool manager //and if so, checks that interest rate mode selected and passed by the user is stable //and user has enough collateral in ETH based on their invoice lending pool collateral reserves/token balance calculateUserReserveCollateralETHInvoicePool(_reserve, _amount, _interestRateMode); // 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; //true if ETH is 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) isNotInvoiceLendingPool(_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 Populous 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 use 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 _receivePToken true if the liquidators wants to receive the PTokens, false if * liquidator wants to receive the underlying asset directly **/ function liquidationCall( address _collateral, address _reserve, address _user, uint256 _purchaseAmount, bool _receivePToken ) 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, _receivePToken ) ); 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://populous.world * @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 PTokenAddress, 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 currentPTokenBalance, 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"); } }
pragma solidity ^0.5.0; import "./Ownable.sol"; import "./InitializableAdminUpgradeabilityProxy.sol"; import "./AddressStorage.sol"; import "./ILendingPoolAddressesProvider.sol"; /** * LendingPoolAddressesProvider contract * - * Is the main registry of the protocol. All the different components of the protocol are accessible * through the addresses provider. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ /** */ 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); } } }
pragma solidity ^0.5.0; import "./SafeMath.sol"; import "./ERC20Detailed.sol"; import "./VersionedInitializable.sol"; import "./LendingPoolAddressesProvider.sol"; import "./LendingPoolCore.sol"; import "./PToken.sol"; /** * LendingPoolConfigurator contract * - * Executes configuration methods on the LendingPoolCore contract. Allows to enable/disable reserves, * and set different protocol parameters. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ contract LendingPoolConfigurator is VersionedInitializable { using SafeMath for uint256; /** * @dev emitted when a reserve is initialized. * @param _reserve the address of the reserve * @param _PToken the address of the overlying PToken contract * @param _interestRateStrategyAddress the address of the interest rate strategy for the reserve **/ event ReserveInitialized( address indexed _reserve, address indexed _PToken, address _interestRateStrategyAddress ); /** * @dev emitted when a reserve is removed. * @param _reserve the address of the reserve **/ event ReserveRemoved(address indexed _reserve); /** * @dev emitted when borrowing is enabled on a reserve * @param _reserve the address of the reserve * @param _stableRateEnabled true if stable rate borrowing is enabled, false otherwise **/ event BorrowingEnabledOnReserve(address _reserve, bool _stableRateEnabled); /** * @dev emitted when borrowing is disabled on a reserve * @param _reserve the address of the reserve **/ event BorrowingDisabledOnReserve(address indexed _reserve); /** * @dev emitted when a reserve is enabled as collateral. * @param _reserve the address of the reserve * @param _ltv 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 **/ event ReserveEnabledAsCollateral( address indexed _reserve, uint256 _ltv, uint256 _liquidationThreshold, uint256 _liquidationBonus ); /** * @dev emitted when a reserve is disabled as collateral * @param _reserve the address of the reserve **/ event ReserveDisabledAsCollateral(address indexed _reserve); /** * @dev emitted when stable rate borrowing is enabled on a reserve * @param _reserve the address of the reserve **/ event StableRateEnabledOnReserve(address indexed _reserve); /** * @dev emitted when stable rate borrowing is disabled on a reserve * @param _reserve the address of the reserve **/ event StableRateDisabledOnReserve(address indexed _reserve); /** * @dev emitted when a reserve is activated * @param _reserve the address of the reserve **/ event ReserveActivated(address indexed _reserve); /** * @dev emitted when a reserve is deactivated * @param _reserve the address of the reserve **/ event ReserveDeactivated(address indexed _reserve); /** * @dev emitted when a reserve is freezed * @param _reserve the address of the reserve **/ event ReserveFreezed(address indexed _reserve); /** * @dev emitted when a reserve is unfreezed * @param _reserve the address of the reserve **/ event ReserveUnfreezed(address indexed _reserve); /** * @dev emitted when a reserve loan to value is updated * @param _reserve the address of the reserve * @param _ltv the new value for the loan to value **/ event ReserveBaseLtvChanged(address _reserve, uint256 _ltv); /** * @dev emitted when a reserve liquidation threshold is updated * @param _reserve the address of the reserve * @param _threshold the new value for the liquidation threshold **/ event ReserveLiquidationThresholdChanged( address _reserve, uint256 _threshold ); /** * @dev emitted when a reserve liquidation bonus is updated * @param _reserve the address of the reserve * @param _bonus the new value for the liquidation bonus **/ event ReserveLiquidationBonusChanged(address _reserve, uint256 _bonus); /** * @dev emitted when the reserve decimals are updated * @param _reserve the address of the reserve * @param _decimals the new decimals **/ event ReserveDecimalsChanged(address _reserve, uint256 _decimals); /** * @dev emitted when a reserve interest strategy contract is updated * @param _reserve the address of the reserve * @param _strategy the new address of the interest strategy contract **/ event ReserveInterestRateStrategyChanged( address _reserve, address _strategy ); LendingPoolAddressesProvider public poolAddressesProvider; //address of the GBP reserve/tokens to be used as collateral to borrow based on invoice lending pool strategy. //can change to a mapping in future if more tokens are to be allowed to borrow from invoice pool reserves address[] public invoiceCollateralReservesList; mapping(bytes32 => address) public invoiceCollateralReserveMap; mapping(address => bool) public isInvoiceCollateralReserve; mapping(address => bool) public isInvoiceReserve; //invoice lending pool reserves /** * @dev only the lending pool manager can call functions affected by this modifier **/ modifier onlyLendingPoolManager { require( poolAddressesProvider.getLendingPoolManager() == msg.sender, "The caller must be a lending pool manager" ); _; } uint256 public constant CONFIGURATOR_REVISION = 0x5; function getRevision() internal pure returns (uint256) { return CONFIGURATOR_REVISION; } function initialize(LendingPoolAddressesProvider _poolAddressesProvider) public initializer { poolAddressesProvider = _poolAddressesProvider; } function getInvoiceCollateralReserves() external view returns (address[] memory) { return invoiceCollateralReservesList; } function getCollateralTokenAddressStatus(address _token) external view returns (bool) { return isInvoiceCollateralReserve[_token]; } //returns the invoice collateral reserve address function getCollateralTokenAddress(bytes32 symbol) external view returns (address) { return invoiceCollateralReserveMap[symbol]; } function disableCollateralTokenAddress(address _token) external onlyLendingPoolManager returns (bool) { require(_token != address(0x0), "address is invalid zero address"); require(isInvoiceCollateralReserve[_token] = true, "token not enabled for use as invoice lending pool collateral"); isInvoiceCollateralReserve[_token] = false; return true; } /** * @dev sets reserve e.g., GBP address to use in a stable borrowing for invoice lending pool reserves * @param _token the address of the reserve to be initialized * @param symbol the token symbol to use as the key **/ function setTokenAddress(bytes32 symbol, address _token) external onlyLendingPoolManager returns (bool) { require(_token != address(0x0), "address is invalid zero address"); invoiceCollateralReserveMap[symbol] = _token; isInvoiceCollateralReserve[_token] = true; bool reserveAlreadyAdded = false; for (uint256 i = 0; i < invoiceCollateralReservesList.length; i++) if (invoiceCollateralReservesList[i] == _token) { //this will check for addresses that have already been added! which works well when calculating user total collateral balance in ETH reserveAlreadyAdded = true; } if (!reserveAlreadyAdded) invoiceCollateralReservesList.push(_token); return true; } /** * @dev checks whether a reserve has been set as stable borrow only with GBP collateral only for invoice lending pool * @param _reserve the address of the reserve to be initialized * @return true/false depending on whether the reserve the reserve address is set as an invoice reserve **/ function isReserveEnabledForInvoicePool(address _reserve) external view returns (bool) { return isInvoiceReserve[_reserve]; } /** * @dev sets a reserve as stable borrow only with GBP collateral only for invoice lending pool * @param _reserve the address of the reserve to be initialized **/ function enableReserveForInvoicePool(address _reserve, bool _enable) external onlyLendingPoolManager { require(_reserve != address(0x0), "reserve address is invalid"); if(_enable == true) { require(isInvoiceReserve[_reserve] == false, "reserve is already been enabled for invoice lending pool"); isInvoiceReserve[_reserve] = true; } else { require(isInvoiceReserve[_reserve] == true, "reserve is not enabled for invoice lending pool"); isInvoiceReserve[_reserve] = false; } } /** * @dev initializes a reserve * @param _reserve the address of the reserve to be initialized * @param _underlyingAssetDecimals the decimals of the reserve underlying asset * @param _interestRateStrategyAddress the address of the interest rate strategy contract for this reserve **/ function initReserve( address _reserve, uint8 _underlyingAssetDecimals, address _interestRateStrategyAddress ) external onlyLendingPoolManager { ERC20Detailed asset = ERC20Detailed(_reserve); string memory PTokenName = string( abi.encodePacked("Populous Interest bearing ", asset.name()) ); string memory PTokenSymbol = string( abi.encodePacked("P", asset.symbol()) ); initReserveWithData( _reserve, PTokenName, PTokenSymbol, _underlyingAssetDecimals, _interestRateStrategyAddress ); } /** * @dev initializes a reserve using PTokenData provided externally (useful if the underlying ERC20 contract doesn't expose name or decimals) * @param _reserve the address of the reserve to be initialized * @param _PTokenName the name of the PToken contract * @param _PTokenSymbol the symbol of the PToken contract * @param _underlyingAssetDecimals the decimals of the reserve underlying asset * @param _interestRateStrategyAddress the address of the interest rate strategy contract for this reserve **/ function initReserveWithData( address _reserve, string memory _PTokenName, string memory _PTokenSymbol, uint8 _underlyingAssetDecimals, address _interestRateStrategyAddress ) public onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); PToken PTokenInstance = new PToken( poolAddressesProvider, _reserve, _underlyingAssetDecimals, _PTokenName, _PTokenSymbol ); core.initReserve( _reserve, address(PTokenInstance), _underlyingAssetDecimals, _interestRateStrategyAddress ); emit ReserveInitialized( _reserve, address(PTokenInstance), _interestRateStrategyAddress ); } /** * @dev removes the last added reserve in the list of the reserves * @param _reserveToRemove the address of the reserve **/ function removeLastAddedReserve(address _reserveToRemove) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.removeLastAddedReserve(_reserveToRemove); emit ReserveRemoved(_reserveToRemove); } /** * @dev enables borrowing on a reserve * @param _reserve the address of the reserve * @param _stableBorrowRateEnabled true if stable borrow rate needs to be enabled by default on this reserve **/ function enableBorrowingOnReserve( address _reserve, bool _stableBorrowRateEnabled ) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.enableBorrowingOnReserve(_reserve, _stableBorrowRateEnabled); emit BorrowingEnabledOnReserve(_reserve, _stableBorrowRateEnabled); } /** * @dev disables borrowing on a reserve * @param _reserve the address of the reserve **/ function disableBorrowingOnReserve(address _reserve) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.disableBorrowingOnReserve(_reserve); emit BorrowingDisabledOnReserve(_reserve); } /** * @dev enables a reserve to be used as collateral * @param _reserve the address of the reserve * @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 enableReserveAsCollateral( address _reserve, uint256 _baseLTVasCollateral, uint256 _liquidationThreshold, uint256 _liquidationBonus ) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.enableReserveAsCollateral( _reserve, _baseLTVasCollateral, _liquidationThreshold, _liquidationBonus ); emit ReserveEnabledAsCollateral( _reserve, _baseLTVasCollateral, _liquidationThreshold, _liquidationBonus ); } /** * @dev disables a reserve as collateral * @param _reserve the address of the reserve **/ function disableReserveAsCollateral(address _reserve) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.disableReserveAsCollateral(_reserve); emit ReserveDisabledAsCollateral(_reserve); } /** * @dev enable stable rate borrowing on a reserve * @param _reserve the address of the reserve **/ function enableReserveStableBorrowRate(address _reserve) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.enableReserveStableBorrowRate(_reserve); emit StableRateEnabledOnReserve(_reserve); } /** * @dev disable stable rate borrowing on a reserve * @param _reserve the address of the reserve **/ function disableReserveStableBorrowRate(address _reserve) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.disableReserveStableBorrowRate(_reserve); emit StableRateDisabledOnReserve(_reserve); } /** * @dev activates a reserve * @param _reserve the address of the reserve **/ function activateReserve(address _reserve) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.activateReserve(_reserve); emit ReserveActivated(_reserve); } /** * @dev deactivates a reserve * @param _reserve the address of the reserve **/ function deactivateReserve(address _reserve) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); require( core.getReserveTotalLiquidity(_reserve) == 0, "The liquidity of the reserve needs to be 0" ); core.deactivateReserve(_reserve); emit ReserveDeactivated(_reserve); } /** * @dev freezes a reserve. A freezed reserve doesn't accept any new deposit, borrow or rate swap, but can accept repayments, liquidations, rate rebalances and redeems * @param _reserve the address of the reserve **/ function freezeReserve(address _reserve) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.freezeReserve(_reserve); emit ReserveFreezed(_reserve); } /** * @dev unfreezes a reserve * @param _reserve the address of the reserve **/ function unfreezeReserve(address _reserve) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.unfreezeReserve(_reserve); emit ReserveUnfreezed(_reserve); } /** * @dev emitted when a reserve loan to value is updated * @param _reserve the address of the reserve * @param _ltv the new value for the loan to value **/ function setReserveBaseLTVasCollateral(address _reserve, uint256 _ltv) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.setReserveBaseLTVasCollateral(_reserve, _ltv); emit ReserveBaseLtvChanged(_reserve, _ltv); } /** * @dev updates the liquidation threshold of a reserve. * @param _reserve the address of the reserve * @param _threshold the new value for the liquidation threshold **/ function setReserveLiquidationThreshold( address _reserve, uint256 _threshold ) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.setReserveLiquidationThreshold(_reserve, _threshold); emit ReserveLiquidationThresholdChanged(_reserve, _threshold); } /** * @dev updates the liquidation bonus of a reserve * @param _reserve the address of the reserve * @param _bonus the new value for the liquidation bonus **/ function setReserveLiquidationBonus(address _reserve, uint256 _bonus) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.setReserveLiquidationBonus(_reserve, _bonus); emit ReserveLiquidationBonusChanged(_reserve, _bonus); } /** * @dev updates the reserve decimals * @param _reserve the address of the reserve * @param _decimals the new number of decimals **/ function setReserveDecimals(address _reserve, uint256 _decimals) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.setReserveDecimals(_reserve, _decimals); emit ReserveDecimalsChanged(_reserve, _decimals); } /** * @dev sets the interest rate strategy of a reserve * @param _reserve the address of the reserve * @param _rateStrategyAddress the new address of the interest strategy contract **/ function setReserveInterestRateStrategyAddress( address _reserve, address _rateStrategyAddress ) external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.setReserveInterestRateStrategyAddress( _reserve, _rateStrategyAddress ); emit ReserveInterestRateStrategyChanged(_reserve, _rateStrategyAddress); } /** * @dev refreshes the lending pool core configuration to update the cached address **/ function refreshLendingPoolCoreConfiguration() external onlyLendingPoolManager { LendingPoolCore core = LendingPoolCore( poolAddressesProvider.getLendingPoolCore() ); core.refreshConfiguration(); } }
pragma solidity ^0.5.0; import "./SafeMath.sol"; import "./SafeERC20.sol"; import "./ERC20.sol"; import "./Address.sol"; import "./VersionedInitializable.sol"; import "./CoreLibrary.sol"; import "./LendingPoolAddressesProvider.sol"; import "./ILendingRateOracle.sol"; import "./IReserveInterestRateStrategy.sol"; import "./WadRayMath.sol"; import "./EthAddressLib.sol"; //import "../tokenization/PToken.sol"; import "./IPToken.sol"; /** *LendingPoolCore contract * - * Holds the state of the lending pool and all the funds deposited * 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. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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 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 _liquidatorReceivesPToken true if the liquidator will receive PTokens, false otherwise **/ function updateStateOnLiquidation( address _principalReserve, address _collateralReserve, address _user, uint256 _amountToLiquidate, uint256 _collateralToLiquidate, uint256 _feeLiquidated, uint256 _liquidatedCollateralForFee, uint256 _balanceIncrease, bool _liquidatorReceivesPToken ) external onlyLendingPool { updatePrincipalReserveStateOnLiquidationInternal( _principalReserve, _user, _amountToLiquidate, _balanceIncrease ); updateCollateralReserveStateOnLiquidationInternal( _collateralReserve ); updateUserStateOnLiquidationInternal( _principalReserve, _user, _amountToLiquidate, _feeLiquidated, _balanceIncrease ); updateReserveInterestRatesAndTimestampInternal(_principalReserve, _amountToLiquidate, 0); if (!_liquidatorReceivesPToken) { 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 PToken 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) { IPToken PToken = IPToken(reserves[_reserve].PTokenAddress); return PToken.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 PToken contract address for the reserve * @param _reserve the reserve address * @return the address of the PToken contract **/ function getReservePTokenAddress(address _reserve) public view returns (address) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; return reserve.PTokenAddress; } /** * @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 _PTokenAddress the address of the overlying PToken contract * @param _decimals the decimals of the reserve currency * @param _interestRateStrategyAddress the address of the interest rate strategy contract **/ function initReserve( address _reserve, address _PTokenAddress, uint256 _decimals, address _interestRateStrategyAddress ) external onlyLendingPoolConfigurator { reserves[_reserve].init(_PTokenAddress, _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].PTokenAddress = 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); } }
pragma solidity ^0.5.0; import "./SafeMath.sol"; import "./VersionedInitializable.sol"; import "./CoreLibrary.sol"; import "./LendingPoolAddressesProvider.sol"; import "./WadRayMath.sol"; import "./IPriceOracleGetter.sol"; import "./IFeeProvider.sol"; import "./PToken.sol"; import "./LendingPoolConfigurator.sol"; import "./LendingPoolCore.sol"; /** * LendingPoolDataProvider contract * - *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 * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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 = 0x5; 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; } //calculates a users collateral in ETH for a specific reserve function calculateUserReserveCollateralETH(address _reserve, address _user) public view returns ( uint256 totalCollateralBalanceETH ) { 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; vars.currentReserve = _reserve; ( vars.compoundedLiquidityBalance, vars.compoundedBorrowBalance, vars.originationFee, vars.userUsesReserveAsCollateral ) = core.getUserBasicReserveData(vars.currentReserve, _user); if ( vars.compoundedLiquidityBalance != 0 && vars.compoundedBorrowBalance != 0 ) { return 0; } //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); if ( vars.usageAsCollateralEnabled && vars.userUsesReserveAsCollateral ) { totalCollateralBalanceETH = totalCollateralBalanceETH.add( liquidityBalanceETH ); } } } //calculates a users collateral in ETH for invoice pool collateral reserves with status = true function calculateUserInvoiceCollateralETH(address _user) public view returns ( uint256 totalCollateralBalanceETH ) { IPriceOracleGetter oracle = IPriceOracleGetter( addressesProvider.getPriceOracle() ); LendingPoolConfigurator poolConfig = LendingPoolConfigurator( addressesProvider.getLendingPoolConfigurator() ); // Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables UserGlobalDataLocalVars memory vars; address[] memory reserves = poolConfig.getInvoiceCollateralReserves();//array of invoice lending pool collateral reserves for (uint256 i = 0; i < reserves.length; i++) { vars.currentReserve = reserves[i]; //check if reserve is enabled or disabled for use as collateral in invoice lending pool if (poolConfig.getCollateralTokenAddressStatus(vars.currentReserve) == true) { ( 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); if ( vars.usageAsCollateralEnabled && vars.userUsesReserveAsCollateral ) { totalCollateralBalanceETH = totalCollateralBalanceETH.add( liquidityBalanceETH ); } } } } } 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 PTokenAddress, 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 ); PTokenAddress = core.getReservePTokenAddress(_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 currentPTokenBalance, uint256 currentBorrowBalance, uint256 principalBorrowBalance, uint256 borrowRateMode, uint256 borrowRate, uint256 liquidityRate, uint256 originationFee, uint256 variableBorrowIndex, uint256 lastUpdateTimestamp, bool usageAsCollateralEnabled ) { currentPTokenBalance = PToken(core.getReservePTokenAddress(_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 ); } }
pragma solidity ^0.5.0; import "./SafeMath.sol"; import "./ReentrancyGuard.sol"; import "./Address.sol"; import "./VersionedInitializable.sol"; import "./LendingPoolAddressesProvider.sol"; import "./LendingPoolParametersProvider.sol"; import "./PToken.sol"; import "./CoreLibrary.sol"; import "./WadRayMath.sol"; import "./LendingPoolCore.sol"; import "./LendingPoolDataProvider.sol"; import "./IPriceOracleGetter.sol"; /** * LendingPoolLiquidationManager contract * - *Implements the liquidation function. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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 _receivePToken true if the liquidator wants to receive PTokens, 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 _receivePToken, 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 _receivePToken true if the liquidators wants to receive the PTokens, false if * he wants to receive the underlying asset directly **/ function liquidationCall( address _collateral, address _reserve, address _user, uint256 _purchaseAmount, bool _receivePToken ) 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 (!_receivePToken) { 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, _receivePToken ); PToken collateralPToken = PToken( core.getReservePTokenAddress(_collateral) ); //if liquidator reclaims the PToken, he receives the equivalent PToken amount if (_receivePToken) { collateralPToken.transferOnLiquidation( _user, msg.sender, maxCollateralToLiquidate ); } else { //otherwise receives the underlying asset //burn the equivalent amount of PToken collateralPToken.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 //PTokens of the user collateralPToken.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, _receivePToken, //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); } }
pragma solidity ^0.5.0; import "./VersionedInitializable.sol"; import "./UintStorage.sol"; /** * LendingPoolParametersProvider contract * - * stores the configuration parameters of the Lending Pool contract * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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); } }
pragma solidity ^0.5.0; import "./ERC20.sol"; /** * @title ERC20Mintable * @dev ERC20 minting logic */ contract MintableERC20 is ERC20 { /** * @dev Function to mint tokens * @param value The amount of tokens to mint. * @return A boolean that indicates if the operation was successful. */ function mint(uint256 value) public returns (bool) { _mint(msg.sender, value); return true; } function burn(uint256 amount) public returns (bool) { _burn(msg.sender, amount); return true; } }
pragma solidity ^0.5.0; import "./MintableERC20.sol"; contract MockUSDC is MintableERC20 { uint256 public decimals = 6; string public symbol = "USDC"; string public name = "USD Coin"; }
pragma solidity ^0.5.0; import "./Context.sol"; /** * @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 { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @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; } }
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() 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()); } }
pragma solidity ^0.5.0; import "./ERC20.sol"; import "./ERC20Detailed.sol"; import "./LendingPoolCore.sol"; import "./LendingPoolAddressesProvider.sol"; import "./LendingPool.sol"; import "./LendingPoolDataProvider.sol"; import "./WadRayMath.sol"; /** * Populous ERC20 PToken * - * Implementation of the interest bearing token for the DLP protocol. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ contract PToken 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 PToken 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 PTokens * @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 PToken * only lending pools can call this function * @param _from the address from which transfer the PTokens * @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 PToken * 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 PTokens, invoked by both _transfer() and * transferOnLiquidation() * @param _from the address from which transfer the PTokens * @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 PTokens * @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; } } }
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. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. * * _Since v2.5.0:_ this module is now much more gas efficient, given net gas * metering changes introduced in the Istanbul hardfork. */ contract ReentrancyGuard { bool private _notEntered; constructor () internal { // Storing an initial non-zero value makes deployment a bit more // expensive, but in exchange the refund on every call to nonReentrant // will be lower in amount. Since refunds are capped to a percetange of // the total transaction's gas, it is best to keep them low in cases // like this one, to increase the likelihood of the full refund coming // into effect. _notEntered = true; } /** * @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() { // On the first call to nonReentrant, _notEntered will be true require(_notEntered, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _notEntered = false; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _notEntered = true; } }
pragma solidity ^0.5.0; import "./IERC20.sol"; import "./SafeMath.sol"; import "./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.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; 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; } }
pragma solidity ^0.5.0; import "./BaseUpgradeabilityProxy.sol"; /** * @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); } } }
pragma solidity >=0.4.24 <0.6.0; /** * VersionedInitializable * - * 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. * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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; }
pragma solidity ^0.5.0; import "./SafeMath.sol"; /** * WadRayMath library * - * Provides mul and div function for wads (decimal numbers with 18 digits precision) and rays (decimals with 27 digits) * - * This contract was cloned from Populous and modified to work with the Populous World eco-system. **/ 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; function ray() internal pure returns (uint256) { return RAY; } function wad() internal pure returns (uint256) { return WAD; } function halfRay() internal pure returns (uint256) { return halfRAY; } function halfWad() internal pure returns (uint256) { return halfWAD; } function wadMul(uint256 a, uint256 b) internal pure returns (uint256) { return halfWAD.add(a.mul(b)).div(WAD); } function wadDiv(uint256 a, uint256 b) internal pure returns (uint256) { uint256 halfB = b / 2; return halfB.add(a.mul(WAD)).div(b); } function rayMul(uint256 a, uint256 b) internal pure returns (uint256) { return halfRAY.add(a.mul(b)).div(RAY); } function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) { uint256 halfB = b / 2; return halfB.add(a.mul(RAY)).div(b); } function rayToWad(uint256 a) internal pure returns (uint256) { uint256 halfRatio = WAD_RAY_RATIO / 2; return halfRatio.add(a).div(WAD_RAY_RATIO); } 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); } } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_USDpToken","type":"address"},{"internalType":"address","name":"_PAXToken","type":"address"},{"internalType":"address","name":"_lendingPool","type":"address"},{"internalType":"address","name":"_lendingPoolCore","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Borrowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CollateralDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CollateralWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Repaid","type":"event"},{"constant":true,"inputs":[],"name":"PAX","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UINT_MAX_VALUE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"USDp","outputs":[{"internalType":"contract MockUSDC","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"borrow","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lendingPool","outputs":[{"internalType":"contract LendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lendingPoolAddressesProvider","outputs":[{"internalType":"contract LendingPoolAddressesProvider","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lendingPoolCore","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lendingPoolDataProvider","outputs":[{"internalType":"contract LendingPoolDataProvider","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"repay","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b5060405162002b1638038062002b16833981810160405260808110156200003757600080fd5b81019080805190602001909291908051906020019092919080519060200190929190805190602001909291905050506000620000786200044c60201b60201c565b9050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614158015620001815750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b8015620001bb5750600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614155b8015620001f55750600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b6200024c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603381526020018062002ae36033913960400191505060405180910390fd5b83600660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050505062000454565b600033905090565b61267f80620004646000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80638f32d59b11610097578063c5ebeaec11610066578063c5ebeaec14610348578063cf8cab9014610376578063d0fc81d2146103c0578063f2fde38b146103de576100f5565b80638f32d59b14610264578063a59a997314610286578063b6b55f25146102d0578063bba00ba5146102fe576100f5565b8063410c3f4c116100d3578063410c3f4c1461017c5780636f8746b6146101c6578063715018a6146102105780638da5cb5b1461021a576100f5565b80630d7ff887146100fa578063371fd8e6146101445780633ccfd60b14610172575b600080fd5b610102610422565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101706004803603602081101561015a57600080fd5b8101908080359060200190929190505050610448565b005b61017a610761565b005b610184610d7c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101ce610da2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610218610dc8565b005b610222610f01565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61026c610f2a565b604051808215151515815260200191505060405180910390f35b61028e610f88565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102fc600480360360208110156102e657600080fd5b8101908080359060200190929190505050610fae565b005b610306611557565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103746004803603602081101561035e57600080fd5b810190808035906020019092919050505061157d565b005b61037e61211e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103c8612144565b6040518082815260200191505060405180910390f35b610420600480360360208110156103f457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612168565b005b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610450610f2a565b6104c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000309050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16846040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561059257600080fd5b505af11580156105a6573d6000803e3d6000fd5b505050506040513d60208110156105bc57600080fd5b810190808051906020019092919050505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635ceae9c4600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684846040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b1580156106cd57600080fd5b505af11580156106e1573d6000803e3d6000fd5b505050506106ed610761565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f0516911bcc3a0a7412a44601057c0a0a1ec628bde049a84284bc428866534488836040518082815260200191505060405180910390a25050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806107cd575061079e610f01565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b610822576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806126016029913960400191505060405180910390fd5b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166335ea6a75600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019150506101a06040518083038186803b1580156108e657600080fd5b505afa1580156108fa573d6000803e3d6000fd5b505050506040513d6101a081101561091157600080fd5b8101908080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190505050909192939495969798999a9b50909192939495969798999a5090919293949596979899509091929394959697985090919293949596975090919293949596509091929394955090919293945090919293509091925090915050809150506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610a9857600080fd5b505afa158015610aac573d6000803e3d6000fd5b505050506040513d6020811015610ac257600080fd5b810190808051906020019092919050505090508173ffffffffffffffffffffffffffffffffffffffff1663db006a757fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610b4857600080fd5b505af1158015610b5c573d6000803e3d6000fd5b505050506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610c0157600080fd5b505afa158015610c15573d6000803e3d6000fd5b505050506040513d6020811015610c2b57600080fd5b810190808051906020019092919050505090506000610c5383836121ee90919063ffffffff16565b9050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342966c68826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015610cca57600080fd5b505af1158015610cde573d6000803e3d6000fd5b505050506040513d6020811015610cf457600080fd5b810190808051906020019092919050505050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fc30fcfbcaac9e0deffa719714eaa82396ff506a0d0d0eebe170830177288715d826040518082815260200191505060405180910390a250505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610dd0610f2a565b610e42576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16610f6c612238565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061101a5750610feb610f01565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b61106f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806126016029913960400191505060405180910390fd5b80600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561110f57600080fd5b505afa158015611123573d6000803e3d6000fd5b505050506040513d602081101561113957600080fd5b810190808051906020019092919050505010156112ed57600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a0712d6861127a600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561123057600080fd5b505afa158015611244573d6000803e3d6000fd5b505050506040513d602081101561125a57600080fd5b8101908080519060200190929190505050846121ee90919063ffffffff16565b6040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156112b057600080fd5b505af11580156112c4573d6000803e3d6000fd5b505050506040513d60208110156112da57600080fd5b8101908080519060200190929190505050505b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156113b857600080fd5b505af11580156113cc573d6000803e3d6000fd5b505050506040513d60208110156113e257600080fd5b810190808051906020019092919050505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d2d0e066600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360006040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018381526020018261ffff1681526020019350505050600060405180830381600087803b1580156114cc57600080fd5b505af11580156114e0573d6000803e3d6000fd5b50505050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fd7243f6f8212d5188fd054141cf6ea89cfc0d91facb8c3afe2f88a1358480142826040518082815260200191505060405180910390a250565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611585610f2a565b6115f7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbeefc3c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561166157600080fd5b505afa158015611675573d6000803e3d6000fd5b505050506040513d602081101561168b57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1663e563a7d030846040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060206040518083038186803b15801561172057600080fd5b505afa158015611734573d6000803e3d6000fd5b505050506040513d602081101561174a57600080fd5b81019080805190602001909291905050509050600080600080600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632c6d0e9b306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019150506101006040518083038186803b15801561180357600080fd5b505afa158015611817573d6000803e3d6000fd5b505050506040513d61010081101561182e57600080fd5b8101908080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291905050505050509450945094509450506000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166312737c33600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1689898888886040518763ffffffff1660e01b8152600401808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001868152602001858152602001848152602001838152602001828152602001965050505050505060206040518083038186803b15801561197c57600080fd5b505afa158015611990573d6000803e3d6000fd5b505050506040513d60208110156119a657600080fd5b8101908080519060200190929190505050905084811115611fac576000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015611a2b57600080fd5b505afa158015611a3f573d6000803e3d6000fd5b505050506040513d6020811015611a5557600080fd5b810190808051906020019092919050505090506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015611ad257600080fd5b505afa158015611ae6573d6000803e3d6000fd5b505050506040513d6020811015611afc57600080fd5b8101908080519060200190929190505050600a0a905060008273ffffffffffffffffffffffffffffffffffffffff1663b3596f07600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611bb357600080fd5b505afa158015611bc7573d6000803e3d6000fd5b505050506040513d6020811015611bdd57600080fd5b810190808051906020019092919050505090506000611c1783611c09848861224090919063ffffffff16565b6122c690919063ffffffff16565b9050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b158015611ce457600080fd5b505af1158015611cf8573d6000803e3d6000fd5b505050506040513d6020811015611d0e57600080fd5b81019080805190602001909291905050505080600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611dc057600080fd5b505afa158015611dd4573d6000803e3d6000fd5b505050506040513d6020811015611dea57600080fd5b81019080805190602001909291905050501015611f9e57600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a0712d68611f2b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611ee157600080fd5b505afa158015611ef5573d6000803e3d6000fd5b505050506040513d6020811015611f0b57600080fd5b8101908080519060200190929190505050846121ee90919063ffffffff16565b6040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015611f6157600080fd5b505af1158015611f75573d6000803e3d6000fd5b505050506040513d6020811015611f8b57600080fd5b8101908080519060200190929190505050505b611fa781610fae565b505050505b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c858f5f9600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1689600160006040518563ffffffff1660e01b8152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018381526020018261ffff168152602001945050505050600060405180830381600087803b15801561208d57600080fd5b505af11580156120a1573d6000803e3d6000fd5b50505050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fac59582e5396aca512fa873a2047e7f4c80f8f55d4a06cb34a78a0187f62719f886040518082815260200191505060405180910390a250505050505050565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81565b612170610f2a565b6121e2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6121eb81612310565b50565b600061223083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612454565b905092915050565b600033905090565b60008083141561225357600090506122c0565b600082840290508284828161226457fe5b04146122bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061262a6021913960400191505060405180910390fd5b809150505b92915050565b600061230883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612514565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612396576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806125db6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000838311158290612501576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156124c65780820151818401526020810190506124ab565b50505050905090810190601f1680156124f35780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b600080831182906125c0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561258557808201518184015260208101905061256a565b50505050905090810190601f1680156125b25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816125cc57fe5b04905080915050939250505056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737363616c6c65722073686f756c6420626520636f6e747261637420697473656c66206f72206f776e6572536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a265627a7a72315820537a0fd2c96c82a3c99fa09be263251f1aa15386be951796a33ca60aafd0d6a764736f6c63430005100032496e76616c6964207a65726f20616464726573732070617373656420617320636f6e7374727563746f7220617267756d656e74000000000000000000000000027e79b5d86a7459bab2c345785a55540741a07a0000000000000000000000008e870d67f660d95d5be530380d0ec0bd388289e1000000000000000000000000efc272451cfdce282befbfe96038484c4c194235000000000000000000000000616b43fc44dd94d236b92d3d04d35d496903af75
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100f55760003560e01c80638f32d59b11610097578063c5ebeaec11610066578063c5ebeaec14610348578063cf8cab9014610376578063d0fc81d2146103c0578063f2fde38b146103de576100f5565b80638f32d59b14610264578063a59a997314610286578063b6b55f25146102d0578063bba00ba5146102fe576100f5565b8063410c3f4c116100d3578063410c3f4c1461017c5780636f8746b6146101c6578063715018a6146102105780638da5cb5b1461021a576100f5565b80630d7ff887146100fa578063371fd8e6146101445780633ccfd60b14610172575b600080fd5b610102610422565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101706004803603602081101561015a57600080fd5b8101908080359060200190929190505050610448565b005b61017a610761565b005b610184610d7c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101ce610da2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610218610dc8565b005b610222610f01565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61026c610f2a565b604051808215151515815260200191505060405180910390f35b61028e610f88565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102fc600480360360208110156102e657600080fd5b8101908080359060200190929190505050610fae565b005b610306611557565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103746004803603602081101561035e57600080fd5b810190808035906020019092919050505061157d565b005b61037e61211e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103c8612144565b6040518082815260200191505060405180910390f35b610420600480360360208110156103f457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612168565b005b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610450610f2a565b6104c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000309050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16846040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561059257600080fd5b505af11580156105a6573d6000803e3d6000fd5b505050506040513d60208110156105bc57600080fd5b810190808051906020019092919050505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635ceae9c4600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684846040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019350505050600060405180830381600087803b1580156106cd57600080fd5b505af11580156106e1573d6000803e3d6000fd5b505050506106ed610761565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f0516911bcc3a0a7412a44601057c0a0a1ec628bde049a84284bc428866534488836040518082815260200191505060405180910390a25050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806107cd575061079e610f01565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b610822576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806126016029913960400191505060405180910390fd5b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166335ea6a75600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019150506101a06040518083038186803b1580156108e657600080fd5b505afa1580156108fa573d6000803e3d6000fd5b505050506040513d6101a081101561091157600080fd5b8101908080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190505050909192939495969798999a9b50909192939495969798999a5090919293949596979899509091929394959697985090919293949596975090919293949596509091929394955090919293945090919293509091925090915050809150506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610a9857600080fd5b505afa158015610aac573d6000803e3d6000fd5b505050506040513d6020811015610ac257600080fd5b810190808051906020019092919050505090508173ffffffffffffffffffffffffffffffffffffffff1663db006a757fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610b4857600080fd5b505af1158015610b5c573d6000803e3d6000fd5b505050506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610c0157600080fd5b505afa158015610c15573d6000803e3d6000fd5b505050506040513d6020811015610c2b57600080fd5b810190808051906020019092919050505090506000610c5383836121ee90919063ffffffff16565b9050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342966c68826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015610cca57600080fd5b505af1158015610cde573d6000803e3d6000fd5b505050506040513d6020811015610cf457600080fd5b810190808051906020019092919050505050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fc30fcfbcaac9e0deffa719714eaa82396ff506a0d0d0eebe170830177288715d826040518082815260200191505060405180910390a250505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610dd0610f2a565b610e42576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16610f6c612238565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061101a5750610feb610f01565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b61106f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806126016029913960400191505060405180910390fd5b80600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561110f57600080fd5b505afa158015611123573d6000803e3d6000fd5b505050506040513d602081101561113957600080fd5b810190808051906020019092919050505010156112ed57600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a0712d6861127a600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561123057600080fd5b505afa158015611244573d6000803e3d6000fd5b505050506040513d602081101561125a57600080fd5b8101908080519060200190929190505050846121ee90919063ffffffff16565b6040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156112b057600080fd5b505af11580156112c4573d6000803e3d6000fd5b505050506040513d60208110156112da57600080fd5b8101908080519060200190929190505050505b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156113b857600080fd5b505af11580156113cc573d6000803e3d6000fd5b505050506040513d60208110156113e257600080fd5b810190808051906020019092919050505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d2d0e066600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360006040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018381526020018261ffff1681526020019350505050600060405180830381600087803b1580156114cc57600080fd5b505af11580156114e0573d6000803e3d6000fd5b50505050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fd7243f6f8212d5188fd054141cf6ea89cfc0d91facb8c3afe2f88a1358480142826040518082815260200191505060405180910390a250565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611585610f2a565b6115f7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbeefc3c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561166157600080fd5b505afa158015611675573d6000803e3d6000fd5b505050506040513d602081101561168b57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1663e563a7d030846040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060206040518083038186803b15801561172057600080fd5b505afa158015611734573d6000803e3d6000fd5b505050506040513d602081101561174a57600080fd5b81019080805190602001909291905050509050600080600080600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632c6d0e9b306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019150506101006040518083038186803b15801561180357600080fd5b505afa158015611817573d6000803e3d6000fd5b505050506040513d61010081101561182e57600080fd5b8101908080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291905050505050509450945094509450506000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166312737c33600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1689898888886040518763ffffffff1660e01b8152600401808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001868152602001858152602001848152602001838152602001828152602001965050505050505060206040518083038186803b15801561197c57600080fd5b505afa158015611990573d6000803e3d6000fd5b505050506040513d60208110156119a657600080fd5b8101908080519060200190929190505050905084811115611fac576000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015611a2b57600080fd5b505afa158015611a3f573d6000803e3d6000fd5b505050506040513d6020811015611a5557600080fd5b810190808051906020019092919050505090506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015611ad257600080fd5b505afa158015611ae6573d6000803e3d6000fd5b505050506040513d6020811015611afc57600080fd5b8101908080519060200190929190505050600a0a905060008273ffffffffffffffffffffffffffffffffffffffff1663b3596f07600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611bb357600080fd5b505afa158015611bc7573d6000803e3d6000fd5b505050506040513d6020811015611bdd57600080fd5b810190808051906020019092919050505090506000611c1783611c09848861224090919063ffffffff16565b6122c690919063ffffffff16565b9050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b158015611ce457600080fd5b505af1158015611cf8573d6000803e3d6000fd5b505050506040513d6020811015611d0e57600080fd5b81019080805190602001909291905050505080600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611dc057600080fd5b505afa158015611dd4573d6000803e3d6000fd5b505050506040513d6020811015611dea57600080fd5b81019080805190602001909291905050501015611f9e57600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a0712d68611f2b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611ee157600080fd5b505afa158015611ef5573d6000803e3d6000fd5b505050506040513d6020811015611f0b57600080fd5b8101908080519060200190929190505050846121ee90919063ffffffff16565b6040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015611f6157600080fd5b505af1158015611f75573d6000803e3d6000fd5b505050506040513d6020811015611f8b57600080fd5b8101908080519060200190929190505050505b611fa781610fae565b505050505b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c858f5f9600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1689600160006040518563ffffffff1660e01b8152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018381526020018261ffff168152602001945050505050600060405180830381600087803b15801561208d57600080fd5b505af11580156120a1573d6000803e3d6000fd5b50505050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fac59582e5396aca512fa873a2047e7f4c80f8f55d4a06cb34a78a0187f62719f886040518082815260200191505060405180910390a250505050505050565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81565b612170610f2a565b6121e2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6121eb81612310565b50565b600061223083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612454565b905092915050565b600033905090565b60008083141561225357600090506122c0565b600082840290508284828161226457fe5b04146122bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061262a6021913960400191505060405180910390fd5b809150505b92915050565b600061230883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612514565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612396576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806125db6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000838311158290612501576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156124c65780820151818401526020810190506124ab565b50505050905090810190601f1680156124f35780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b600080831182906125c0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561258557808201518184015260208101905061256a565b50505050905090810190601f1680156125b25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816125cc57fe5b04905080915050939250505056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737363616c6c65722073686f756c6420626520636f6e747261637420697473656c66206f72206f776e6572536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a265627a7a72315820537a0fd2c96c82a3c99fa09be263251f1aa15386be951796a33ca60aafd0d6a764736f6c63430005100032
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000027e79b5d86a7459bab2c345785a55540741a07a0000000000000000000000008e870d67f660d95d5be530380d0ec0bd388289e1000000000000000000000000efc272451cfdce282befbfe96038484c4c194235000000000000000000000000616b43fc44dd94d236b92d3d04d35d496903af75
-----Decoded View---------------
Arg [0] : _USDpToken (address): 0x027E79B5D86A7459bAb2c345785A55540741a07A
Arg [1] : _PAXToken (address): 0x8E870D67F660D95d5be530380D0eC0bd388289E1
Arg [2] : _lendingPool (address): 0xEFc272451cFDCe282BEFBfE96038484C4C194235
Arg [3] : _lendingPoolCore (address): 0x616B43FC44dD94D236B92d3d04D35d496903Af75
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000027e79b5d86a7459bab2c345785a55540741a07a
Arg [1] : 0000000000000000000000008e870d67f660d95d5be530380d0ec0bd388289e1
Arg [2] : 000000000000000000000000efc272451cfdce282befbfe96038484c4c194235
Arg [3] : 000000000000000000000000616b43fc44dd94d236b92d3d04d35d496903af75
Deployed Bytecode Sourcemap
545:5039:20:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;545:5039:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;779:30;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;5156:425;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;5156:425:20;;;;;;;;;;;;;;;;;:::i;:::-;;2873:626;;;:::i;:::-;;816:54;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;950:18;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;1734:140:30;;;:::i;:::-;;923:79;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;1289:94;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;742:30:20;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;2217:648;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2217:648:20;;;;;;;;;;;;;;;;;:::i;:::-;;877:64;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;3507:1641;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;3507:1641:20;;;;;;;;;;;;;;;;;:::i;:::-;;975:20;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;683:52;;;:::i;:::-;;;;;;;;;;;;;;;;;;;2029:109:30;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2029:109:30;;;;;;;;;;;;;;;;;;;:::i;:::-;;779:30:20;;;;;;;;;;;;;:::o;5156:425::-;1135:9:30;:7;:9::i;:::-;1127:54;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5217:27:20;5271:4;5217:61;;5296:3;;;;;;;;;;;5289:19;;;5309:15;;;;;;;;;;;5326:6;5289:44;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;5289:44:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;5289:44:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;5289:44:20;;;;;;;;;;;;;;;;;5344:11;;;;;;;;;;;:17;;;5362:3;;;;;;;;;;;5367:6;5375:11;5344:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;5344:43:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;5344:43:20;;;;5528:10;:8;:10::i;:::-;5561:3;;;;;;;;;;;5554:19;;;5566:6;5554:19;;;;;;;;;;;;;;;;;;1192:1:30;5156:425:20;:::o;2873:626::-;1354:4;1332:27;;:10;:27;;;:52;;;;1377:7;:5;:7::i;:::-;1363:21;;:10;:21;;;1332:52;1324:106;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2929:21;3004:11;;;;;;;;;;;:26;;;3039:4;;;;;;;;;;;3004:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3004:41:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3004:41:20;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;3004:41:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2961:84;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3056:23;3082:4;;;;;;;;;;;:14;;;3105:4;3082:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3082:29:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3082:29:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;3082:29:20;;;;;;;;;;;;;;;;3056:55;;3213:13;3206:28;;;732:2;3206:44;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3206:44:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3206:44:20;;;;3261:22;3286:4;;;;;;;;;;;:14;;;3309:4;3286:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3286:29:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3286:29:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;3286:29:20;;;;;;;;;;;;;;;;3261:54;;3326:23;3352:35;3371:15;3352:14;:18;;:35;;;;:::i;:::-;3326:61;;3398:4;;;;;;;;;;;:9;;;3408:15;3398:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3398:26:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3398:26:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;3398:26:20;;;;;;;;;;;;;;;;;3468:4;;;;;;;;;;;3440:51;;;3475:15;3440:51;;;;;;;;;;;;;;;;;;1441:1;;;;2873:626::o;816:54::-;;;;;;;;;;;;;:::o;950:18::-;;;;;;;;;;;;;:::o;1734:140:30:-;1135:9;:7;:9::i;:::-;1127:54;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1833:1;1796:40;;1817:6;;;;;;;;;;;1796:40;;;;;;;;;;;;1864:1;1847:6;;:19;;;;;;;;;;;;;;;;;;1734:140::o;923:79::-;961:7;988:6;;;;;;;;;;;981:13;;923:79;:::o;1289:94::-;1329:4;1369:6;;;;;;;;;;;1353:22;;:12;:10;:12::i;:::-;:22;;;1346:29;;1289:94;:::o;742:30:20:-;;;;;;;;;;;;;:::o;2217:648::-;1354:4;1332:27;;:10;:27;;;:52;;;;1377:7;:5;:7::i;:::-;1363:21;;:10;:21;;;1332:52;1324:106;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2407:6;2375:4;;;;;;;;;;;:14;;;2398:4;2375:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2375:29:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2375:29:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2375:29:20;;;;;;;;;;;;;;;;:38;2371:123;;;2430:4;;;;;;;;;;;:9;;;2440:41;2451:4;;;;;;;;;;;:14;;;2474:4;2451:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2451:29:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2451:29:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2451:29:20;;;;;;;;;;;;;;;;2440:6;:10;;:41;;;;:::i;:::-;2430:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2430:52:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2430:52:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2430:52:20;;;;;;;;;;;;;;;;;2371:123;2557:4;;;;;;;;;;;:12;;;2570:15;;;;;;;;;;;2587:6;2557:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2557:37:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2557:37:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2557:37:20;;;;;;;;;;;;;;;;;2723:11;;;;;;;;;;;:19;;;2751:4;;;;;;;;;;;2758:6;2766:1;2723:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2723:45:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2723:45:20;;;;2843:4;;;;;;;;;;;2815:42;;;2850:6;2815:42;;;;;;;;;;;;;;;;;;2217:648;:::o;877:64::-;;;;;;;;;;;;;:::o;3507:1641::-;1135:9:30;:7;:9::i;:::-;1127:54;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3569:17:20;3602:28;;;;;;;;;;;:43;;;:45;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3602:45:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3602:45:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;3602:45:20;;;;;;;;;;;;;;;;3589:87;;;3685:4;3692:6;3589:110;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3589:110:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3589:110:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;3589:110:20;;;;;;;;;;;;;;;;3569:130;;3741:33;3789:29;3833:20;3868:18;3945:23;;;;;;;;;;;:47;;;4001:4;3945:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3945:62:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3945:62:20;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;3945:62:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3710:297;;;;;;;;;;;;4020:35;4058:23;;;;;;;;;;;:54;;;4128:3;;;;;;;;;;;4146:6;4167:9;4191:21;4227:12;4254:10;4058:217;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4058:217:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4058:217:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4058:217:20;;;;;;;;;;;;;;;;4020:255;;4322:25;4292:27;:55;4288:766;;;4364:25;4429:28;;;;;;;;;;;:43;;;:45;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4429:45:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4429:45:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4429:45:20;;;;;;;;;;;;;;;;4364:125;;4506:17;4530:4;;;;;;;;;;;:13;;;:15;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4530:15:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4530:15:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4530:15:20;;;;;;;;;;;;;;;;4526:2;:19;4506:39;;4560:24;4587:6;:20;;;4616:4;;;;;;;;;;;4587:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4587:35:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4587:35:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4587:35:20;;;;;;;;;;;;;;;;4560:62;;4637:27;4667:90;4747:9;4667:49;4699:16;4667:27;:31;;:49;;;;:::i;:::-;:53;;:90;;;;:::i;:::-;4637:120;;4774:4;;;;;;;;;;;:12;;;4787:15;;;;;;;;;;;4804:19;4774:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4774:50:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4774:50:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4774:50:20;;;;;;;;;;;;;;;;;4877:19;4845:4;;;;;;;;;;;:14;;;4868:4;4845:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4845:29:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4845:29:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4845:29:20;;;;;;;;;;;;;;;;:51;4841:157;;;4917:4;;;;;;;;;;;:9;;;4927:54;4951:4;;;;;;;;;;;:14;;;4974:4;4951:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4951:29:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4951:29:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4951:29:20;;;;;;;;;;;;;;;;4927:19;:23;;:54;;;;:::i;:::-;4917:65;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4917:65:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4917:65:20;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4917:65:20;;;;;;;;;;;;;;;;;4841:157;5014:28;5022:19;5014:7;:28::i;:::-;4288:766;;;;;5066:11;;;;;;;;;;;:18;;;5085:3;;;;;;;;;;;5090:6;5098:1;5101;5066:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;5066:37:20;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;5066:37:20;;;;5128:3;;;;;;;;;;;5119:21;;;5133:6;5119:21;;;;;;;;;;;;;;;;;;1192:1:30;;;;;;3507:1641:20;:::o;975:20::-;;;;;;;;;;;;;:::o;683:52::-;732:2;683:52;:::o;2029:109:30:-;1135:9;:7;:9::i;:::-;1127:54;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2102:28;2121:8;2102:18;:28::i;:::-;2029:109;:::o;1315:136:35:-;1373:7;1400:43;1404:1;1407;1400:43;;;;;;;;;;;;;;;;;:3;:43::i;:::-;1393:50;;1315:136;;;;:::o;806:98:4:-;851:15;886:10;879:17;;806:98;:::o;2231:471:35:-;2289:7;2539:1;2534;:6;2530:47;;;2564:1;2557:8;;;;2530:47;2589:9;2605:1;2601;:5;2589:17;;2634:1;2629;2625;:5;;;;;;:10;2617:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2693:1;2686:8;;;2231:471;;;;;:::o;3170:132::-;3228:7;3255:39;3259:1;3262;3255:39;;;;;;;;;;;;;;;;;:3;:39::i;:::-;3248:46;;3170:132;;;;:::o;2244:229:30:-;2338:1;2318:22;;:8;:22;;;;2310:73;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2428:8;2399:38;;2420:6;;;;;;;;;;;2399:38;;;;;;;;;;;;2457:8;2448:6;;:17;;;;;;;;;;;;;;;;;;2244:229;:::o;1788:192:35:-;1874:7;1907:1;1902;:6;;1910:12;1894:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;1894:29:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1934:9;1950:1;1946;:5;1934:17;;1971:1;1964:8;;;1788:192;;;;;:::o;3832:345::-;3918:7;4017:1;4013;:5;4020:12;4005:28;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;4005:28:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4044:9;4060:1;4056;:5;;;;;;4044:17;;4168:1;4161:8;;;3832:345;;;;;:::o
Swarm Source
bzzr://537a0fd2c96c82a3c99fa09be263251f1aa15386be951796a33ca60aafd0d6a7
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.