ERC-20
Overview
Max Total Supply
98,583.156013053055275646 dUNI
Holders
13
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Balance
259.184479465987677075 dUNIValue
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Name:
VariableDebtToken
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity Multiple files format)
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IVariableDebtToken} from "./IVariableDebtToken.sol"; import {WadRayMath} from "./WadRayMath.sol"; import {Errors} from "./Errors.sol"; import {DebtTokenBase} from "./DebtTokenBase.sol"; import {SafeMath} from "./SafeMath.sol"; import { IMarginPoolAddressesProvider } from "./IMarginPoolAddressesProvider.sol"; import {IERC20} from "./IERC20.sol"; import {SafeERC20} from "./SafeERC20.sol"; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2); } } /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier * available, which can be aplied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. */ contract ReentrancyGuard { /// @dev counter to allow mutex lock with only one SSTORE operation uint256 private _guardCounter; constructor() internal { // The counter starts at one to prevent changing it from zero to a non-zero // value, which is a more expensive operation. _guardCounter = 1; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { _guardCounter += 1; uint256 localCounter = _guardCounter; _; require( localCounter == _guardCounter, "ReentrancyGuard: reentrant call" ); } } /** * @title VariableDebtToken * @notice Implements a variable debt token to track the borrowing positions of users * at variable rate mode * @author Lever **/ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken, ReentrancyGuard { using WadRayMath for uint256; using SafeERC20 for IERC20; uint256 public constant DEBT_TOKEN_REVISION = 0x1; // address public rewardsDistribution; IERC20 public rewardsToken; uint256 public periodFinish = 0; uint256 public rewardRate = 0; uint256 public rewardsDuration = 30 days; uint256 public lastUpdateTime; uint256 public rewardPerTokenStored; mapping(address => uint256) public userRewardPerTokenPaid; mapping(address => uint256) public rewards; IMarginPoolAddressesProvider public addressesProvider; constructor( address _addressesProvider, address underlyingAsset, string memory name, string memory symbol, uint8 decimals ) public DebtTokenBase( IMarginPoolAddressesProvider(_addressesProvider).getMarginPool(), underlyingAsset, name, symbol, decimals ) { // rewardsDistribution = IMarginPoolAddressesProvider(_addressesProvider).getRewardsDistribution(); addressesProvider = IMarginPoolAddressesProvider(_addressesProvider); rewardsToken = IERC20(IMarginPoolAddressesProvider(_addressesProvider).getLeverToken()); } /* ========== MODIFIERS ========== */ modifier updateReward(address account) { rewardPerTokenStored = rewardPerToken(); lastUpdateTime = lastTimeRewardApplicable(); if (account != address(0)) { rewards[account] = earned(account); userRewardPerTokenPaid[account] = rewardPerTokenStored; } _; } modifier onlyRewardsDistribution() { require( msg.sender == addressesProvider.getRewardsDistribution(), "Caller is not RewardsDistribution contract" ); _; } /** * @dev Calculates the accumulated debt balance of the user * @return The debt balance of the user **/ function balanceOf(address user) public view virtual override returns (uint256) { uint256 scaledBalance = super.balanceOf(user); if (scaledBalance == 0) { return 0; } return scaledBalance.rayMul( POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS) ); } /** * @dev Mints debt token to the `onBehalfOf` address * - Only callable by the MarginPool * @param user The address receiving the borrowed underlying, being the delegatee in case * of credit delegate, or same as `onBehalfOf` otherwise * @param onBehalfOf The address receiving the debt tokens * @param amount The amount of debt being minted * @param index The variable debt index of the reserve * @return `true` if the the previous balance of the user is 0 **/ function mint( address user, address onBehalfOf, uint256 amount, uint256 index ) external override onlyMarginPool updateReward(onBehalfOf) returns (bool) { if (user != onBehalfOf) { _decreaseBorrowAllowance(onBehalfOf, user, amount); } uint256 previousBalance = super.balanceOf(onBehalfOf); uint256 amountScaled = amount.rayDiv(index); require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT); _mint(onBehalfOf, amountScaled); emit Transfer(address(0), onBehalfOf, amount); emit Mint(user, onBehalfOf, amount, index); return previousBalance == 0; } /** * @dev Burns user variable debt * - Only callable by the MarginPool * @param user The user whose debt is getting burned * @param amount The amount getting burned * @param index The variable debt index of the reserve **/ function burn( address user, uint256 amount, uint256 index ) external override onlyMarginPool updateReward(user) { uint256 amountScaled = amount.rayDiv(index); require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT); _burn(user, amountScaled); emit Transfer(user, address(0), amount); emit Burn(user, amount, index); } /** * @dev Returns the principal debt balance of the user from * @return The debt balance of the user since the last burn/mint action **/ function scaledBalanceOf(address user) public view virtual override returns (uint256) { return super.balanceOf(user); } /** * @dev Returns the total supply of the variable debt token. Represents the total debt accrued by the users * @return The total supply **/ function totalSupply() public view virtual override returns (uint256) { return super.totalSupply().rayMul( POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS) ); } /** * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) * @return the scaled total supply **/ function scaledTotalSupply() public view virtual override returns (uint256) { return super.totalSupply(); } /** * @dev Returns the principal balance of the user and principal total supply. * @param user The address of the user * @return The principal balance of the user * @return The principal total supply **/ function getScaledUserBalanceAndSupply(address user) external view override returns (uint256, uint256) { return (super.balanceOf(user), super.totalSupply()); } function _transfer( address from, address to, uint256 amount ) internal override updateReward(from) updateReward(to) { super._transfer(from, to, amount); } function lastTimeRewardApplicable() public view returns (uint256) { return Math.min(block.timestamp, periodFinish); } function rewardPerToken() public view returns (uint256) { if (totalSupply() == 0) { return rewardPerTokenStored; } return rewardPerTokenStored.add( lastTimeRewardApplicable() .sub(lastUpdateTime) .mul(rewardRate) .mul(1e18) .div(totalSupply()) ); } function earned(address account) public view returns (uint256) { return balanceOf(account) .mul(rewardPerToken().sub(userRewardPerTokenPaid[account])) .div(1e18) .add(rewards[account]); } function getRewardForDuration() external view returns (uint256) { return rewardRate.mul(rewardsDuration); } function getReward() public nonReentrant updateReward(msg.sender) { uint256 reward = rewards[msg.sender]; require(reward > 0); rewards[msg.sender] = 0; rewardsToken.safeTransfer(msg.sender, reward); emit RewardPaid(msg.sender, reward); } /* ========== RESTRICTED FUNCTIONS ========== */ function notifyRewardAmount(uint256 reward, uint256 _rewardsDuration) external onlyRewardsDistribution updateReward(address(0)) { // Ensure the provided reward amount is not more than the balance in the contract. // This keeps the reward rate in the right range, preventing overflows due to // very high values of rewardRate in the earned and rewardsPerToken functions; // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. uint256 balance = rewardsToken.balanceOf(address(this)); if (block.timestamp >= periodFinish) { rewardsDuration = _rewardsDuration; rewardRate = reward.div(rewardsDuration); require( rewardRate <= balance.div(rewardsDuration), "Provided reward too high" ); periodFinish = block.timestamp.add(rewardsDuration); } else { uint256 remaining = periodFinish.sub(block.timestamp); uint256 leftover = remaining.mul(rewardRate); rewardRate = reward.add(leftover).div(remaining); require( rewardRate <= balance.div(remaining), "Provided reward too high" ); } lastUpdateTime = block.timestamp; emit RewardAdded(reward, _rewardsDuration); } /* ========== EVENTS ========== */ event RewardAdded(uint256 reward, uint256 _rewardsDuration); event RewardPaid(address indexed user, uint256 reward); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, 'Address: insufficient balance'); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(''); require(success, 'Address: unable to send value, recipient may have reverted'); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import './BaseAdminUpgradeabilityProxy.sol'; /** * @title AdminUpgradeabilityProxy * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for * initializing the implementation, admin, and init data. */ contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy { /** * Contract constructor. * @param _logic address of the initial implementation. * @param _admin Address of the proxy administrator. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ constructor( address _logic, address _admin, bytes memory _data ) public payable UpgradeabilityProxy(_logic, _data) { assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); _setAdmin(_admin); } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) { BaseAdminUpgradeabilityProxy._willFallback(); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; 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, "upgradeToAndCall failed"); } /** * @return adm 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 virtual override { require(msg.sender != _admin(), 'Cannot call fallback function from the proxy admin'); super._willFallback(); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import './BaseUpgradeabilityProxy.sol'; /** * @title BaseImmutableAdminUpgradeabilityProxy * @author Lever, inspired by the OpenZeppelin upgradeability proxy pattern * @dev This contract combines an upgradeability proxy with an authorization * mechanism for administrative tasks. The admin role is stored in an immutable, which * helps saving transactions costs * 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 BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy { address immutable ADMIN; constructor(address admin) public { ADMIN = admin; } 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 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, "upgradeToAndCall failed"); } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal virtual override { require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin'); super._willFallback(); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; 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 impl Address of the current implementation */ function _implementation() internal view override 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) } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; library DataTypes { // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties. struct ReserveData { //stores the reserve configuration ReserveConfigurationMap configuration; //the liquidity index. Expressed in ray uint128 liquidityIndex; //variable borrow index. Expressed in ray uint128 variableBorrowIndex; //the current supply rate. Expressed in ray uint128 currentLiquidityRate; //the current variable borrow rate. Expressed in ray uint128 currentVariableBorrowRate; uint40 lastUpdateTimestamp; //tokens addresses address xTokenAddress; address variableDebtTokenAddress; //address of the interest rate strategy address interestRateStrategyAddress; //the id of the reserve. Represents the position in the list of the active reserves uint8 id; } struct ReserveConfigurationMap { //bit 0-15: LTV //bit 16-31: Liq. threshold //bit 32-47: Liq. bonus //bit 48-55: Decimals //bit 56: Reserve is active //bit 57: reserve is frozen //bit 58: borrowing is enabled //bit 60-63: reserved //bit 64-79: reserve factor uint256 data; } struct UserConfigurationMap { uint256 data; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IMarginPool} from './IMarginPool.sol'; import {ICreditDelegationToken} from './ICreditDelegationToken.sol'; import {IncentivizedERC20} from './IncentivizedERC20.sol'; import {Errors} from './Errors.sol'; /** * @title DebtTokenBase * @author Lever */ abstract contract DebtTokenBase is IncentivizedERC20, ICreditDelegationToken { address public immutable UNDERLYING_ASSET_ADDRESS; IMarginPool public immutable POOL; mapping(address => mapping(address => uint256)) internal _borrowAllowances; /** * @dev Only margin pool can call functions marked by this modifier **/ modifier onlyMarginPool { require(_msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_MARGIN_POOL); _; } /** * @dev The metadata of the token will be set on the proxy, that the reason of * passing "NULL" and 0 as metadata */ constructor( address pool, address underlyingAssetAddress, string memory name, string memory symbol, uint8 decimals ) public IncentivizedERC20(name, symbol, decimals) { POOL = IMarginPool(pool); UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; } /** * @dev delegates borrowing power to a user on the specific debt token * @param delegatee the address receiving the delegated borrowing power * @param amount the maximum amount being delegated. Delegation will still * respect the liquidation constraints (even if delegated, a delegatee cannot * force a delegator HF to go below 1) **/ function approveDelegation(address delegatee, uint256 amount) external override { _borrowAllowances[_msgSender()][delegatee] = amount; emit BorrowAllowanceDelegated(_msgSender(), delegatee, UNDERLYING_ASSET_ADDRESS, amount); } /** * @dev returns the borrow allowance of the user * @param fromUser The user to giving allowance * @param toUser The user to give allowance to * @return the current allowance of toUser **/ function borrowAllowance(address fromUser, address toUser) external view override returns (uint256) { return _borrowAllowances[fromUser][toUser]; } /** * @dev Being non transferrable, the debt token does not implement any of the * standard ERC20 functions for transfer and allowance. **/ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { recipient; amount; revert('TRANSFER_NOT_SUPPORTED'); } function allowance(address owner, address spender) public view virtual override returns (uint256) { owner; spender; revert('ALLOWANCE_NOT_SUPPORTED'); } function approve(address spender, uint256 amount) public virtual override returns (bool) { spender; amount; revert('APPROVAL_NOT_SUPPORTED'); } function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { sender; recipient; amount; revert('TRANSFER_NOT_SUPPORTED'); } function increaseAllowance(address spender, uint256 addedValue) public virtual override returns (bool) { spender; addedValue; revert('ALLOWANCE_NOT_SUPPORTED'); } function decreaseAllowance(address spender, uint256 subtractedValue) public virtual override returns (bool) { spender; subtractedValue; revert('ALLOWANCE_NOT_SUPPORTED'); } function _decreaseBorrowAllowance( address delegator, address delegatee, uint256 amount ) internal { uint256 newAllowance = _borrowAllowances[delegator][delegatee].sub(amount, Errors.BORROW_ALLOWANCE_NOT_ENOUGH); _borrowAllowances[delegator][delegatee] = newAllowance; emit BorrowAllowanceDelegated(delegator, delegatee, UNDERLYING_ASSET_ADDRESS, newAllowance); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {SafeMath} from './SafeMath.sol'; import {IReserveInterestRateStrategy} from './IReserveInterestRateStrategy.sol'; import {WadRayMath} from './WadRayMath.sol'; import {PercentageMath} from './PercentageMath.sol'; import {IMarginPoolAddressesProvider} from './IMarginPoolAddressesProvider.sol'; /** * @title DefaultReserveInterestRateStrategy contract * @notice Implements the calculation of the interest rates depending on the reserve state * @dev The model of interest rate is based on 2 slopes, one before the `OPTIMAL_UTILIZATION_RATE` * point of utilization and another from that one to 100% * @author Lever **/ contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { using WadRayMath for uint256; using SafeMath for uint256; using PercentageMath 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 immutable OPTIMAL_UTILIZATION_RATE; /** * @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 immutable EXCESS_UTILIZATION_RATE; IMarginPoolAddressesProvider public immutable addressesProvider; // Base variable borrow rate when Utilization rate = 0. Expressed in ray uint256 internal immutable _baseVariableBorrowRate; // Slope of the variable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray uint256 internal immutable _variableRateSlope1; // Slope of the variable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray uint256 internal immutable _variableRateSlope2; constructor( IMarginPoolAddressesProvider provider, uint256 optimalUtilizationRate, uint256 baseVariableBorrowRate, uint256 variableRateSlope1, uint256 variableRateSlope2 ) public { OPTIMAL_UTILIZATION_RATE = optimalUtilizationRate; EXCESS_UTILIZATION_RATE = WadRayMath.ray().sub(optimalUtilizationRate); addressesProvider = provider; _baseVariableBorrowRate = baseVariableBorrowRate; _variableRateSlope1 = variableRateSlope1; _variableRateSlope2 = variableRateSlope2; } function variableRateSlope1() external view returns (uint256) { return _variableRateSlope1; } function variableRateSlope2() external view returns (uint256) { return _variableRateSlope2; } function baseVariableBorrowRate() external view override returns (uint256) { return _baseVariableBorrowRate; } function getMaxVariableBorrowRate() external view override returns (uint256) { return _baseVariableBorrowRate.add(_variableRateSlope1).add(_variableRateSlope2); } struct CalcInterestRatesLocalVars { uint256 totalDebt; uint256 currentVariableBorrowRate; uint256 currentLiquidityRate; uint256 utilizationRate; } /** * @dev Calculates the interest rates depending on the reserve's state and configurations * @param availableLiquidity The liquidity available in the reserve * @param totalVariableDebt The total borrowed from the reserve at a variable rate * @param reserveFactor The reserve portion of the interest that goes to the treasury address. * @return The liquidity rate, the variable borrow rate **/ function calculateInterestRates( uint256 availableLiquidity, uint256 totalVariableDebt, uint256 reserveFactor ) external view override returns ( uint256, uint256 ) { CalcInterestRatesLocalVars memory vars; vars.totalDebt = totalVariableDebt; vars.currentVariableBorrowRate = 0; vars.currentLiquidityRate = 0; uint256 utilizationRate = vars.totalDebt == 0 ? 0 : vars.totalDebt.rayDiv(availableLiquidity.add(vars.totalDebt)); if (utilizationRate > OPTIMAL_UTILIZATION_RATE) { uint256 excessUtilizationRateRatio = utilizationRate.sub(OPTIMAL_UTILIZATION_RATE).rayDiv(EXCESS_UTILIZATION_RATE); vars.currentVariableBorrowRate = _baseVariableBorrowRate.add(_variableRateSlope1).add( _variableRateSlope2.rayMul(excessUtilizationRateRatio) ); } else { vars.currentVariableBorrowRate = _baseVariableBorrowRate.add( utilizationRate.rayMul(_variableRateSlope1).rayDiv(OPTIMAL_UTILIZATION_RATE) ); } vars.currentLiquidityRate = _getOverallBorrowRate( totalVariableDebt, vars.currentVariableBorrowRate ) .rayMul(utilizationRate) .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(reserveFactor)); return ( vars.currentLiquidityRate, vars.currentVariableBorrowRate ); } /** * @dev Calculates the overall borrow rate as the weighted average between the total variable debt * @param totalVariableDebt The total borrowed from the reserve at a variable rate * @param currentVariableBorrowRate The current variable borrow rate of the reserve * @return The weighted averaged borrow rate **/ function _getOverallBorrowRate( uint256 totalVariableDebt, uint256 currentVariableBorrowRate ) internal pure returns (uint256) { if (totalVariableDebt == 0) return 0; uint256 weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate); uint256 overallBorrowRate = weightedVariableRate.rayDiv(totalVariableDebt.wadToRay()); return overallBorrowRate; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import './Context.sol'; import './IERC20.sol'; import './SafeMath.sol'; import './Address.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 {ERC20PresetMinterPauser}. * * 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; using Address for address; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor(string memory name, string memory symbol) public { _name = name; _symbol = symbol; _decimals = 18; } /** * @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. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override 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 virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override 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 virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual 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 virtual { require(sender != address(0), 'ERC20: transfer from the zero address'); require(recipient != address(0), 'ERC20: transfer to the zero address'); _beforeTokenTransfer(sender, recipient, amount); _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 virtual { require(account != address(0), 'ERC20: mint to the zero address'); _beforeTokenTransfer(address(0), account, amount); _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 virtual { require(account != address(0), 'ERC20: burn from the zero address'); _beforeTokenTransfer(account, address(0), amount); _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 virtual { 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 Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @title Errors library * @author Lever * @notice Defines the error messages emitted by the different contracts of the Lever protocol * @dev Error messages prefix glossary: * - VL = ValidationLogic * - MATH = Math libraries * - CT = Common errors between tokens (XToken, VariableDebtToken) * - XT = XToken * - DT = VariableDebtToken * - MP = MarginPool * - MPAPR = MarginPoolAddressesProviderRegistry * - MPC = MarginPoolConfiguration * - RL = ReserveLogic * - MPCM = MarginPoolCollateralManager * - P = Pausable */ library Errors { //common errors string public constant CALLER_NOT_POOL_ADMIN = '33'; // 'The caller must be the pool admin' string public constant BORROW_ALLOWANCE_NOT_ENOUGH = '59'; // User borrows on behalf, but allowance are too small //contract specific errors string public constant VL_INVALID_AMOUNT = '1'; // 'Amount must be greater than 0' string public constant VL_NO_ACTIVE_RESERVE = '2'; // 'Action requires an active reserve' string public constant VL_RESERVE_FROZEN = '3'; // 'Action cannot be performed because the reserve is frozen' string public constant VL_CURRENT_AVAILABLE_LIQUIDITY_NOT_ENOUGH = '4'; // 'The current liquidity is not enough' string public constant VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = '5'; // 'User cannot withdraw more than the available balance' string public constant VL_TRANSFER_NOT_ALLOWED = '6'; // 'Transfer cannot be allowed.' string public constant VL_BORROWING_NOT_ENABLED = '7'; // 'Borrowing is not enabled' string public constant VL_INVALID_INTEREST_RATE_MODE_SELECTED = '8'; // 'Invalid interest rate mode selected' string public constant VL_COLLATERAL_BALANCE_IS_0 = '9'; // 'The collateral balance is 0' string public constant VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '10'; // 'Health factor is lesser than the liquidation threshold' string public constant VL_COLLATERAL_CANNOT_COVER_NEW_BORROW = '11'; // 'There is not enough collateral to cover a new borrow' string public constant VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY = '13'; // collateral is (mostly) the same currency that is being borrowed string public constant VL_NO_DEBT_OF_SELECTED_TYPE = '15'; // 'he needs to have variable debt' string public constant VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '16'; // 'To repay on behalf of an user an explicit amount to repay is needed' string public constant VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE = '18'; // 'User does not have a variable rate loan in progress on this reserve' string public constant VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0 = '19'; // 'The underlying balance needs to be greater than 0' string public constant VL_DEPOSIT_ALREADY_IN_USE = '20'; // 'User deposit is already being used as collateral' string public constant MP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '22'; // 'Interest rate rebalance conditions were not met' string public constant MP_LIQUIDATION_CALL_FAILED = '23'; // 'Liquidation call failed' string public constant MP_NOT_ENOUGH_LIQUIDITY_TO_BORROW = '24'; // 'There is not enough liquidity available to borrow' string public constant MP_REQUESTED_AMOUNT_TOO_SMALL = '25'; // 'The requested amount is too small for a FlashLoan.' string public constant MP_INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent' string public constant MP_CALLER_NOT_MARGIN_POOL_CONFIGURATOR = '27'; // 'The caller of the function is not the margin pool configurator' string public constant MP_INCONSISTENT_FLASHLOAN_PARAMS = '28'; string public constant CT_CALLER_MUST_BE_MARGIN_POOL = '29'; // 'The caller of this function must be a margin pool' string public constant CT_CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself' string public constant CT_TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero' string public constant RL_RESERVE_ALREADY_INITIALIZED = '32'; // 'Reserve has already been initialized' string public constant MPC_RESERVE_LIQUIDITY_NOT_0 = '34'; // 'The liquidity of the reserve needs to be 0' string public constant MPC_INVALID_XTOKEN_POOL_ADDRESS = '35'; // 'The liquidity of the reserve needs to be 0' string public constant MPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS = '37'; // 'The liquidity of the reserve needs to be 0' string public constant MPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '39'; // 'The liquidity of the reserve needs to be 0' string public constant MPC_INVALID_ADDRESSES_PROVIDER_ID = '40'; // 'The liquidity of the reserve needs to be 0' string public constant MPC_INVALID_CONFIGURATION = '75'; // 'Invalid risk parameters for the reserve' string public constant MPC_CALLER_NOT_EMERGENCY_ADMIN = '76'; // 'The caller must be the emergency admin' string public constant MPAPR_PROVIDER_NOT_REGISTERED = '41'; // 'Provider is not registered' string public constant MPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '42'; // 'Health factor is not below the threshold' string public constant MPCM_COLLATERAL_CANNOT_BE_LIQUIDATED = '43'; // 'The collateral chosen cannot be liquidated' string public constant MPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '44'; // 'User did not borrow the specified currency' string public constant MPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE = '45'; // "There isn't enough liquidity available to liquidate" string public constant MPCM_NO_ERRORS = '46'; // 'No errors' string public constant MP_INVALID_FLASHLOAN_MODE = '47'; //Invalid flashloan mode selected string public constant MATH_MULTIPLICATION_OVERFLOW = '48'; string public constant MATH_ADDITION_OVERFLOW = '49'; string public constant MATH_DIVISION_BY_ZERO = '50'; string public constant RL_LIQUIDITY_INDEX_OVERFLOW = '51'; // Liquidity index overflows uint128 string public constant RL_VARIABLE_BORROW_INDEX_OVERFLOW = '52'; // Variable borrow index overflows uint128 string public constant RL_LIQUIDITY_RATE_OVERFLOW = '53'; // Liquidity rate overflows uint128 string public constant RL_VARIABLE_BORROW_RATE_OVERFLOW = '54'; // Variable borrow rate overflows uint128 string public constant CT_INVALID_MINT_AMOUNT = '56'; //invalid amount to mint string public constant MP_FAILED_REPAY_WITH_COLLATERAL = '57'; string public constant CT_INVALID_BURN_AMOUNT = '58'; //invalid amount to burn string public constant MP_FAILED_COLLATERAL_SWAP = '60'; string public constant MP_INVALID_EQUAL_ASSETS_TO_SWAP = '61'; string public constant MP_REENTRANCY_NOT_ALLOWED = '62'; string public constant MP_CALLER_MUST_BE_AN_XTOKEN = '63'; string public constant MP_IS_PAUSED = '64'; // 'Pool is paused' string public constant MP_NO_MORE_RESERVES_ALLOWED = '65'; string public constant MP_INVALID_FLASH_LOAN_EXECUTOR_RETURN = '66'; string public constant RC_INVALID_LTV = '67'; string public constant RC_INVALID_LIQ_THRESHOLD = '68'; string public constant RC_INVALID_LIQ_BONUS = '69'; string public constant RC_INVALID_DECIMALS = '70'; string public constant RC_INVALID_RESERVE_FACTOR = '71'; string public constant MPAPR_INVALID_ADDRESSES_PROVIDER_ID = '72'; string public constant VL_INCONSISTENT_FLASHLOAN_PARAMS = '73'; string public constant MP_INCONSISTENT_PARAMS_LENGTH = '74'; string public constant UL_INVALID_INDEX = '77'; string public constant MP_NOT_CONTRACT = '78'; string public constant SDT_BURN_EXCEEDS_BALANCE = '80'; enum CollateralManagerErrors { NO_ERROR, NO_COLLATERAL_AVAILABLE, COLLATERAL_CANNOT_BE_LIQUIDATED, CURRRENCY_NOT_BORROWED, HEALTH_FACTOR_ABOVE_THRESHOLD, NOT_ENOUGH_LIQUIDITY, NO_ACTIVE_RESERVE, HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD, INVALID_EQUAL_ASSETS_TO_SWAP, FROZEN_RESERVE } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {SafeMath} from './SafeMath.sol'; import {IERC20} from './IERC20.sol'; import {ReserveLogic} from './ReserveLogic.sol'; import {ReserveConfiguration} from './ReserveConfiguration.sol'; import {UserConfiguration} from './UserConfiguration.sol'; import {WadRayMath} from './WadRayMath.sol'; import {PercentageMath} from './PercentageMath.sol'; import {IPriceOracleGetter} from './IPriceOracleGetter.sol'; import {DataTypes} from './DataTypes.sol'; /** * @title GenericLogic library * @author Lever * @title Implements protocol-level logic to calculate and validate the state of a user */ library GenericLogic { using ReserveLogic for DataTypes.ReserveData; using SafeMath for uint256; using WadRayMath for uint256; using PercentageMath for uint256; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using UserConfiguration for DataTypes.UserConfigurationMap; uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether; struct balanceDecreaseAllowedLocalVars { uint256 decimals; uint256 liquidationThreshold; uint256 totalCollateralInETH; uint256 totalDebtInETH; uint256 avgLiquidationThreshold; uint256 amountToDecreaseInETH; uint256 collateralBalanceAfterDecrease; uint256 liquidationThresholdAfterDecrease; uint256 healthFactorAfterDecrease; bool reserveUsageAsCollateralEnabled; } /** * @dev Checks if a specific balance decrease is allowed * (i.e. doesn't bring the user borrow position health factor under HEALTH_FACTOR_LIQUIDATION_THRESHOLD) * @param asset The address of the underlying asset of the reserve * @param user The address of the user * @param amount The amount to decrease * @param reservesData The data of all the reserves * @param userConfig The user configuration * @param reserves The list of all the active reserves * @param oracle The address of the oracle contract * @return true if the decrease of the balance is allowed **/ function balanceDecreaseAllowed( address asset, address user, uint256 amount, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap calldata userConfig, mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle ) external view returns (bool) { if (!userConfig.isBorrowingAny() || !userConfig.isUsingAsCollateral(reservesData[asset].id)) { return true; } balanceDecreaseAllowedLocalVars memory vars; (, vars.liquidationThreshold, , vars.decimals, ) = reservesData[asset] .configuration .getParams(); if (vars.liquidationThreshold == 0) { return true; } ( vars.totalCollateralInETH, vars.totalDebtInETH, , vars.avgLiquidationThreshold, ) = calculateUserAccountData(user, reservesData, userConfig, reserves, reservesCount, oracle); if (vars.totalDebtInETH == 0) { return true; } vars.amountToDecreaseInETH = IPriceOracleGetter(oracle).getAssetPrice(asset).mul(amount).div( 10**vars.decimals ); vars.collateralBalanceAfterDecrease = vars.totalCollateralInETH.sub(vars.amountToDecreaseInETH); //if there is a borrow, there can't be 0 collateral if (vars.collateralBalanceAfterDecrease == 0) { return false; } vars.liquidationThresholdAfterDecrease = vars .totalCollateralInETH .mul(vars.avgLiquidationThreshold) .sub(vars.amountToDecreaseInETH.mul(vars.liquidationThreshold)) .div(vars.collateralBalanceAfterDecrease); uint256 healthFactorAfterDecrease = calculateHealthFactorFromBalances( vars.collateralBalanceAfterDecrease, vars.totalDebtInETH, vars.liquidationThresholdAfterDecrease ); return healthFactorAfterDecrease >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD; } struct CalculateUserAccountDataVars { uint256 reserveUnitPrice; uint256 tokenUnit; uint256 compoundedLiquidityBalance; uint256 compoundedBorrowBalance; uint256 decimals; uint256 ltv; uint256 liquidationThreshold; uint256 i; uint256 healthFactor; uint256 totalCollateralInETH; uint256 totalDebtInETH; uint256 avgLtv; uint256 avgLiquidationThreshold; uint256 reservesLength; bool healthFactorBelowThreshold; address currentReserveAddress; bool usageAsCollateralEnabled; bool userUsesReserveAsCollateral; } /** * @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 * @param reservesData Data of all the reserves * @param userConfig The configuration of the user * @param reserves The list of the available reserves * @param oracle The price oracle address * @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold and the HF **/ function calculateUserAccountData( address user, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap memory userConfig, mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle ) internal view returns ( uint256, uint256, uint256, uint256, uint256 ) { CalculateUserAccountDataVars memory vars; if (userConfig.isEmpty()) { return (0, 0, 0, 0, uint256(-1)); } for (vars.i = 0; vars.i < reservesCount; vars.i++) { if (!userConfig.isUsingAsCollateralOrBorrowing(vars.i)) { continue; } vars.currentReserveAddress = reserves[vars.i]; DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress]; (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve .configuration .getParams(); vars.tokenUnit = 10**vars.decimals; vars.reserveUnitPrice = IPriceOracleGetter(oracle).getAssetPrice(vars.currentReserveAddress); if (vars.liquidationThreshold != 0 && userConfig.isUsingAsCollateral(vars.i)) { vars.compoundedLiquidityBalance = IERC20(currentReserve.xTokenAddress).balanceOf(user); uint256 liquidityBalanceETH = vars.reserveUnitPrice.mul(vars.compoundedLiquidityBalance).div(vars.tokenUnit); vars.totalCollateralInETH = vars.totalCollateralInETH.add(liquidityBalanceETH); vars.avgLtv = vars.avgLtv.add(liquidityBalanceETH.mul(vars.ltv)); vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add( liquidityBalanceETH.mul(vars.liquidationThreshold) ); } if (userConfig.isBorrowing(vars.i)) { vars.compoundedBorrowBalance = IERC20(currentReserve.variableDebtTokenAddress).balanceOf( user ); vars.totalDebtInETH = vars.totalDebtInETH.add( vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit) ); } } vars.avgLtv = vars.totalCollateralInETH > 0 ? vars.avgLtv.div(vars.totalCollateralInETH) : 0; vars.avgLiquidationThreshold = vars.totalCollateralInETH > 0 ? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH) : 0; vars.healthFactor = calculateHealthFactorFromBalances( vars.totalCollateralInETH, vars.totalDebtInETH, vars.avgLiquidationThreshold ); return ( vars.totalCollateralInETH, vars.totalDebtInETH, vars.avgLtv, vars.avgLiquidationThreshold, vars.healthFactor ); } /** * @dev Calculates the health factor from the corresponding balances * @param totalCollateralInETH The total collateral in ETH * @param totalDebtInETH The total debt in ETH * @param liquidationThreshold The avg liquidation threshold * @return The health factor calculated from the balances provided **/ function calculateHealthFactorFromBalances( uint256 totalCollateralInETH, uint256 totalDebtInETH, uint256 liquidationThreshold ) internal pure returns (uint256) { if (totalDebtInETH == 0) return uint256(-1); return (totalCollateralInETH.percentMul(liquidationThreshold)).wadDiv(totalDebtInETH); } /** * @dev Calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the * average Loan To Value * @param totalCollateralInETH The total collateral in ETH * @param totalDebtInETH The total borrow balance * @param ltv The average loan to value * @return the amount available to borrow in ETH for the user **/ function calculateAvailableBorrowsETH( uint256 totalCollateralInETH, uint256 totalDebtInETH, uint256 ltv ) internal pure returns (uint256) { uint256 availableBorrowsETH = totalCollateralInETH.percentMul(ltv); if (availableBorrowsETH < totalDebtInETH) { return 0; } availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH); return availableBorrowsETH; } struct AvailableCollateralToLiquidateLocalVars { uint256 userCompoundedBorrowBalance; uint256 liquidationBonus; uint256 collateralPrice; uint256 debtAssetPrice; uint256 maxAmountCollateralToLiquidate; uint256 debtAssetDecimals; uint256 collateralDecimals; } /** * @dev Calculates how much of a specific collateral can be liquidated, given * a certain amount of debt asset. * - This function needs to be called after all the checks to validate the liquidation have been performed, * otherwise it might fail. * @param collateralReserve The data of the collateral reserve * @param debtReserve The data of the debt reserve * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover * @param userCollateralBalance The collateral balance for the specific `collateralAsset` of the user being liquidated * @return collateralAmount: The maximum amount that is possible to liquidate given all the liquidation constraints * (user balance, close factor) * debtAmountNeeded: The amount to repay with the liquidation **/ function calculateAvailableCollateralToLiquidate( DataTypes.ReserveData storage collateralReserve, DataTypes.ReserveData storage debtReserve, address collateralAsset, address debtAsset, uint256 debtToCover, uint256 userCollateralBalance, address oracle ) internal view returns (uint256, uint256) { uint256 collateralAmount = 0; AvailableCollateralToLiquidateLocalVars memory vars; vars.collateralPrice = IPriceOracleGetter(oracle).getAssetPrice( collateralAsset ); vars.debtAssetPrice = IPriceOracleGetter(oracle).getAssetPrice( debtAsset ); ( , , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve.configuration.getParams(); vars.debtAssetDecimals = debtReserve.configuration.getDecimals(); // This is the maximum possible amount of the selected collateral that can be liquidated, given the // max amount of liquidatable debt vars.maxAmountCollateralToLiquidate = vars .debtAssetPrice .mul(debtToCover) .mul(10**vars.collateralDecimals) .percentMul(vars.liquidationBonus) .div(vars.collateralPrice.mul(10**vars.debtAssetDecimals)); if (vars.maxAmountCollateralToLiquidate > userCollateralBalance) { collateralAmount = userCollateralBalance; } else { collateralAmount = vars.maxAmountCollateralToLiquidate; } return ( collateralAmount, collateralAmount.percentDiv(vars.liquidationBonus) ); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IERC20} from './IERC20.sol'; import {DataTypes} from './DataTypes.sol'; /** * @title Helpers library * @author Lever */ library Helpers { /** * @dev Fetches the user current variable debt balances * @param user The user address * @param reserve The reserve data object * @return The variable debt balance **/ function getUserCurrentDebt(address user, DataTypes.ReserveData storage reserve) internal view returns (uint256) { return IERC20(reserve.variableDebtTokenAddress).balanceOf(user); } function getUserCurrentDebtMemory(address user, DataTypes.ReserveData memory reserve) internal view returns (uint256) { return IERC20(reserve.variableDebtTokenAddress).balanceOf(user); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; interface IChainlinkAggregator { function latestAnswer() external view returns (int256); function latestTimestamp() external view returns (uint256); function latestRound() external view returns (uint256); function getAnswer(uint256 roundId) external view returns (int256); function getTimestamp(uint256 roundId) external view returns (uint256); event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); event NewRound(uint256 indexed roundId, address indexed startedBy); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; interface ICreditDelegationToken { event BorrowAllowanceDelegated( address indexed fromUser, address indexed toUser, address asset, uint256 amount ); /** * @dev delegates borrowing power to a user on the specific debt token * @param delegatee the address receiving the delegated borrowing power * @param amount the maximum amount being delegated. Delegation will still * respect the liquidation constraints (even if delegated, a delegatee cannot * force a delegator HF to go below 1) **/ function approveDelegation(address delegatee, uint256 amount) external; /** * @dev returns the borrow allowance of the user * @param fromUser The user to giving allowance * @param toUser The user to give allowance to * @return the current allowance of toUser **/ function borrowAllowance(address fromUser, address toUser) external view returns (uint256); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IERC20} from './IERC20.sol'; interface IERC20Detailed is IERC20 { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; contract IERC20DetailedBytes { bytes32 public name; bytes32 public symbol; uint256 public decimals; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IERC20} from './IERC20.sol'; interface IERC20WithPermit is IERC20 { function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; interface IExtendedPriceAggregator { event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); function getToken() external view returns (address); function getTokenType() external view returns (uint256); function getPlatformId() external view returns (uint256); function getSubTokens() external view returns (address[] memory); function latestAnswer() external view returns (int256); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {IMarginPoolAddressesProvider} from './IMarginPoolAddressesProvider.sol'; import {DataTypes} from './DataTypes.sol'; interface IMarginPool { /** * @dev Emitted on deposit() * @param reserve The address of the underlying asset of the reserve * @param user The address initiating the deposit * @param onBehalfOf The beneficiary of the deposit, receiving the xTokens * @param amount The amount deposited **/ event Deposit( address indexed reserve, address indexed user, address indexed onBehalfOf, uint256 amount ); /** * @dev Emitted on withdraw() * @param reserve The address of the underlyng asset being withdrawn * @param user The address initiating the withdrawal, owner of xTokens * @param to Address that will receive the underlying * @param amount The amount to be withdrawn **/ event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); /** * @dev Emitted on borrow() when debt needs to be opened * @param reserve The address of the underlying asset being borrowed * @param user The address of the user initiating the borrow(), receiving the funds on borrow() * @param onBehalfOf The address that will be getting the debt * @param amount The amount borrowed out * @param borrowRate The numeric rate at which the user has borrowed **/ event Borrow( address indexed reserve, address indexed user, address indexed onBehalfOf, uint256 amount, uint256 borrowRate ); /** * @dev Emitted on repay() * @param reserve The address of the underlying asset of the reserve * @param user The beneficiary of the repayment, getting his debt reduced * @param repayer The address of the user initiating the repay(), providing the funds * @param amount The amount repaid **/ event Repay( address indexed reserve, address indexed user, address indexed repayer, uint256 amount ); /** * @dev Emitted on swapTokensForTokens() swapTokensForClosePosition() swapWithAggregation() closeWithAggregation() * @param user The address initiating the swap * @param srcReserve The address of the underlying asset of the source reserve * @param dstReserve The address of the underlying asset of the destination reserve * @param srcAmount The amount of source reserve * @param dstAmount The amount of destination reserve **/ event Swap( address indexed user, address indexed srcReserve, address indexed dstReserve, uint256 srcAmount, uint256 dstAmount ); /** * @dev Emitted on setUserUseReserveAsCollateral() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user enabling the usage as collateral **/ event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); /** * @dev Emitted on setUserUseReserveAsCollateral() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user enabling the usage as collateral **/ event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); /** * @dev Emitted when the pause is triggered. */ event Paused(); /** * @dev Emitted when the pause is lifted. */ event Unpaused(); /** * @dev Emitted when a borrower is liquidated. This event is emitted by the MarginPool via * MarginPoolCollateral manager using a DELEGATECALL * This allows to have the events in the generated ABI for MarginPool. * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param user The address of the borrower getting liquidated * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator * @param liquidator The address of the liquidator **/ event LiquidationCall( address indexed collateralAsset, address indexed debtAsset, address indexed user, uint256 debtToCover, uint256 liquidatedCollateralAmount, address liquidator ); /** * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal, * the event will actually be fired by the MarginPool contract. The event is therefore replicated here so it * gets added to the MarginPool ABI * @param reserve The address of the underlying asset of the reserve * @param liquidityRate The new liquidity rate * @param variableBorrowRate The new variable borrow rate * @param liquidityIndex The new liquidity index * @param variableBorrowIndex The new variable borrow index **/ event ReserveDataUpdated( address indexed reserve, uint256 liquidityRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex ); /** * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying xTokens. * - E.g. User deposits 100 USDC and gets in return 100 xUSDC * @param asset The address of the underlying asset to deposit * @param amount The amount to be deposited * @param onBehalfOf The address that will receive the xTokens, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of xTokens * is a different wallet **/ function deposit( address asset, uint256 amount, address onBehalfOf ) external; /** * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent xTokens owned * E.g. User has 100 xUSDC, calls withdraw() and receives 100 USDC, burning the 100 xUSDC * @param asset The address of the underlying asset to withdraw * @param amount The underlying amount to be withdrawn * - Send the value type(uint256).max in order to withdraw the whole xToken balance * @param to Address that will receive the underlying, same as msg.sender if the user * wants to receive it on his own wallet, or a different address if the beneficiary is a * different wallet * @return The final amount withdrawn **/ function withdraw( address asset, uint256 amount, address to ) external returns (uint256); /** * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower * already deposited enough collateral, or he was given enough allowance by a credit delegator on the * corresponding debt token ( VariableDebtToken) * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet * and 100 variable debt tokens * @param asset The address of the underlying asset to borrow * @param amount The amount to be borrowed * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator * if he has been given credit delegation allowance **/ function borrow( address asset, uint256 amount, address onBehalfOf ) external; /** * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned * - E.g. User repays 100 USDC, burning 100 variable debt tokens of the `onBehalfOf` address * @param asset The address of the borrowed underlying asset previously borrowed * @param amount The amount to repay * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the * user calling the function if he wants to reduce/remove his own debt, or the address of any other * other borrower whose debt should be removed * @return The final amount repaid **/ function repay( address asset, uint256 amount, address onBehalfOf ) external returns (uint256); function swapTokensForTokens( uint256 amountIn, uint256 amountOut, address[] calldata path, bool isExactIn, bool isUni ) external; function swapTokensForClosePosition( uint256 amountIn, uint256 amountOut, address[] calldata path, bool isExactIn, bool isUni ) external; /** * @dev Allows depositors to enable/disable a specific deposited asset as collateral * @param asset The address of the underlying asset deposited * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise **/ function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external; /** * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param user The address of the borrower getting liquidated * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover **/ function liquidationCall( address collateralAsset, address debtAsset, address user, uint256 debtToCover ) external; /** * @dev Returns the user account data across all the reserves * @param user The address of the user * @return totalCollateralETH the total collateral in ETH of the user * @return totalDebtETH the total debt in ETH of the user * @return availableBorrowsETH the borrowing power left of the user * @return currentLiquidationThreshold the liquidation threshold of the user * @return ltv the loan to value of the user * @return healthFactor the current health factor of the user **/ function getUserAccountData(address user) external view returns ( uint256 totalCollateralETH, uint256 totalDebtETH, uint256 availableBorrowsETH, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor ); function initReserve( address reserve, address xTokenAddress, address variableDebtAddress, address interestRateStrategyAddress ) external; function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) external; function setConfiguration(address reserve, uint256 configuration) external; /** * @dev Returns the configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The configuration of the reserve **/ function getConfiguration(address asset) external view returns (DataTypes.ReserveConfigurationMap memory); /** * @dev Returns the configuration of the user across all the reserves * @param user The user address * @return The configuration of the user **/ function getUserConfiguration(address user) external view returns (DataTypes.UserConfigurationMap memory); /** * @dev Returns the normalized income normalized income of the reserve * @param asset The address of the underlying asset of the reserve * @return The reserve's normalized income */ function getReserveNormalizedIncome(address asset) external view returns (uint256); /** * @dev Returns the normalized variable debt per unit of asset * @param asset The address of the underlying asset of the reserve * @return The reserve normalized variable debt */ function getReserveNormalizedVariableDebt(address asset) external view returns (uint256); /** * @dev Returns the state and configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The state of the reserve **/ function getReserveData(address asset) external view returns (DataTypes.ReserveData memory); function finalizeTransfer( address asset, address from, address to, uint256 amount, uint256 balanceFromAfter, uint256 balanceToBefore ) external; function getReservesList() external view returns (address[] memory); function getAddressesProvider() external view returns (IMarginPoolAddressesProvider); function setPause(bool val) external; function paused() external view returns (bool); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @title MarginPoolAddressesProvider contract * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles * - Acting also as factory of proxies and admin of those, so with right to change its implementations * - Owned by the Lever Governance * @author Lever **/ interface IMarginPoolAddressesProvider { event MarginPoolUpdated(address indexed newAddress); event ConfigurationAdminUpdated(address indexed newAddress); event EmergencyAdminUpdated(address indexed newAddress); event MarginPoolConfiguratorUpdated(address indexed newAddress); event MarginPoolCollateralManagerUpdated(address indexed newAddress); event PriceOracleUpdated(address indexed newAddress); event ProxyCreated(bytes32 id, address indexed newAddress); event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy); event LeverTokenUpdated(address indexed newAddress); event TreasuryAddressUpdated(address indexed newAddress); event RewardsDistributionUpdated(address indexed newAddress); event OrderBookUpdated(address indexed newAddress); event SwapMinerUpdated(address indexed newAddress); function setAddress(bytes32 id, address newAddress) external; function setAddressAsProxy(bytes32 id, address impl) external; function getAddress(bytes32 id) external view returns (address); function getMarginPool() external view returns (address); function setMarginPoolImpl(address pool, address UniswapRouter,address SushiswapRouter, address weth) external; function getMarginPoolConfigurator() external view returns (address); function setMarginPoolConfiguratorImpl(address configurator) external; function getPoolAdmin() external view returns (address); function setPoolAdmin(address admin) external; function getEmergencyAdmin() external view returns (address); function setEmergencyAdmin(address admin) external; function getPriceOracle() external view returns (address); function setPriceOracle(address priceOracle) external; function getLeverToken() external view returns (address); function setLeverToken(address lever) external; function getTreasuryAddress() external view returns (address); function setTreasuryAddress(address treasuryAddress) external; function getRewardsDistribution() external view returns (address); function setRewardsDistribution(address rewardsDistribution) external; function getOrderBook() external view returns (address); function setOrderBookImpl(address addressProvider, address UniswapRouter, address weth) external; function getSwapMiner() external view returns (address); function setSwapMinerImpl(address _swapMiner, address UniswapRouter, address _uniswapLevPairToken, address LeverUsdOracle) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {Context} from './Context.sol'; import {IERC20} from './IERC20.sol'; import {IERC20Detailed} from './IERC20Detailed.sol'; import {SafeMath} from './SafeMath.sol'; /** * @title ERC20 * @notice Basic ERC20 implementation * @author Lever, inspired by the Openzeppelin ERC20 implementation **/ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { using SafeMath for uint256; mapping(address => uint256) internal _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 internal _totalSupply; string private _name; string private _symbol; uint8 private _decimals; constructor( string memory name, string memory symbol, uint8 decimals ) public { _name = name; _symbol = symbol; _decimals = decimals; } /** * @return The name of the token **/ function name() public view override returns (string memory) { return _name; } /** * @return The symbol of the token **/ function symbol() public view override returns (string memory) { return _symbol; } /** * @return The decimals of the token **/ function decimals() public view override returns (uint8) { return _decimals; } /** * @return The total supply of the token **/ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @return The balance of the token **/ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev Executes a transfer of tokens from _msgSender() to recipient * @param recipient The recipient of the tokens * @param amount The amount of tokens being transferred * @return `true` if the transfer succeeds, `false` otherwise **/ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); emit Transfer(_msgSender(), recipient, amount); return true; } /** * @dev Returns the allowance of spender on the tokens owned by owner * @param owner The owner of the tokens * @param spender The user allowed to spend the owner's tokens * @return The amount of owner's tokens spender is allowed to spend **/ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev Allows `spender` to spend the tokens owned by _msgSender() * @param spender The user allowed to spend _msgSender() tokens * @return `true` **/ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev Executes a transfer of token from sender to recipient, if _msgSender() is allowed to do so * @param sender The owner of the tokens * @param recipient The recipient of the tokens * @param amount The amount of tokens being transferred * @return `true` if the transfer succeeds, `false` otherwise **/ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve( sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance') ); emit Transfer(sender, recipient, amount); return true; } /** * @dev Increases the allowance of spender to spend _msgSender() tokens * @param spender The user allowed to spend on behalf of _msgSender() * @param addedValue The amount being added to the allowance * @return `true` **/ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Decreases the allowance of spender to spend _msgSender() tokens * @param spender The user allowed to spend on behalf of _msgSender() * @param subtractedValue The amount being subtracted to the allowance * @return `true` **/ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve( _msgSender(), spender, _allowances[_msgSender()][spender].sub( subtractedValue, 'ERC20: decreased allowance below zero' ) ); return true; } function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), 'ERC20: transfer from the zero address'); require(recipient != address(0), 'ERC20: transfer to the zero address'); _beforeTokenTransfer(sender, recipient, amount); uint256 oldSenderBalance = _balances[sender]; _balances[sender] = oldSenderBalance.sub(amount, 'ERC20: transfer amount exceeds balance'); _balances[recipient] = _balances[recipient].add(amount); } function _mint(address account, uint256 amount) internal virtual { require(account != address(0), 'ERC20: mint to the zero address'); _beforeTokenTransfer(address(0), account, amount); uint256 oldTotalSupply = _totalSupply; _totalSupply = oldTotalSupply.add(amount); uint256 oldAccountBalance = _balances[account]; _balances[account] = oldAccountBalance.add(amount); } function _burn(address account, uint256 amount) internal virtual { require(account != address(0), 'ERC20: burn from the zero address'); _beforeTokenTransfer(account, address(0), amount); uint256 oldTotalSupply = _totalSupply; _totalSupply = oldTotalSupply.sub(amount); uint256 oldAccountBalance = _balances[account]; _balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'); } function _approve( address owner, address spender, uint256 amount ) internal virtual { 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); } function _setName(string memory newName) internal { _name = newName; } function _setSymbol(string memory newSymbol) internal { _symbol = newSymbol; } function _setDecimals(uint8 newDecimals) internal { _decimals = newDecimals; } function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: agpl-3.0 pragma solidity >=0.4.24 <0.7.0; /** * @title Initializable * * @dev Helper contract to support initializer functions. To use it, replace * the constructor with a function that has the `initializer` modifier. * WARNING: Unlike constructors, initializer functions must be manually * invoked. This applies both to deploying an Initializable contract, as well * as extending an Initializable contract via inheritance. * WARNING: When used with inheritance, manual care must be taken to not invoke * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. */ contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private initializing; /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { require( initializing || isConstructor() || !initialized, 'Contract instance has already been initialized' ); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; initialized = true; } _; if (isTopLevelCall) { initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function isConstructor() private view returns (bool) { // extcodesize checks the size of the code stored in an address, and // address returns the current address. Since the code is still not // deployed when running a constructor, any checks on its code size will // yield zero, making it an effective way to detect if a contract is // under construction or not. 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; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; 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),"_implementation() != address(0)"); InitializableUpgradeabilityProxy.initialize(logic, data); assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); _setAdmin(admin); } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) { BaseAdminUpgradeabilityProxy._willFallback(); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import './BaseImmutableAdminUpgradeabilityProxy.sol'; import './InitializableUpgradeabilityProxy.sol'; /** * @title InitializableAdminUpgradeabilityProxy * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function */ contract InitializableImmutableAdminUpgradeabilityProxy is BaseImmutableAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy { constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {} /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) { BaseImmutableAdminUpgradeabilityProxy._willFallback(); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; 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),"_implementation error!"); assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); _setImplementation(_logic); if (_data.length > 0) { (bool success, ) = _logic.delegatecall(_data); require(success,"_logic.delegatecall error!"); } } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /************ @title IPriceOracle interface @notice Interface for the Lever price oracle.*/ interface IPriceOracle { /*********** @dev returns the asset price in ETH */ function getAssetPrice(address asset) external view returns (uint256); /*********** @dev sets the asset price, in wei */ function setAssetPrice(address asset, uint256 price) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @title IPriceOracleGetter interface * @notice Interface for the Lever price oracle. **/ interface IPriceOracleGetter { /** * @dev returns the asset price in ETH * @param asset the address of the asset * @return the ETH price of the asset **/ function getAssetPrice(address asset) external view returns (uint256); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @title IReserveInterestRateStrategyInterface interface * @dev Interface for the calculation of the interest rates * @author Lever */ interface IReserveInterestRateStrategy { function baseVariableBorrowRate() external view returns (uint256); function getMaxVariableBorrowRate() external view returns (uint256); function calculateInterestRates( uint256 utilizationRate, uint256 totalVariableDebt, uint256 reserveFactor ) external view returns ( uint256 liquidityRate, uint256 variableBorrowRate ); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; interface IScaledBalanceToken { /** * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the * updated stored balance divided by the reserve's liquidity index at the moment of the update * @param user The user whose balance is calculated * @return The scaled balance of the user **/ function scaledBalanceOf(address user) external view returns (uint256); /** * @dev Returns the scaled balance of the user and the scaled total supply. * @param user The address of the user * @return The scaled balance of the user * @return The scaled balance and the scaled total supply **/ function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256); /** * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) * @return The scaled total supply **/ function scaledTotalSupply() external view returns (uint256); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.6.12; /** * @title ITokenConfiguration * @author Lever * @dev Common interface between xTokens and debt tokens to fetch the * token configuration **/ interface ITokenConfiguration { function UNDERLYING_ASSET_ADDRESS() external view returns (address); function POOL() external view returns (address); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity >=0.6.2; interface IUniswapV2Router01 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns ( uint256 amountA, uint256 amountB, uint256 liquidity ); function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable returns ( uint256 amountToken, uint256 amountETH, uint256 liquidity ); function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETH( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountToken, uint256 amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETHWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountToken, uint256 amountETH); function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactETHForTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function swapTokensForExactETH( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapETHForExactTokens( uint256 amountOut, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) external pure returns (uint256 amountB); function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountOut); function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountIn); function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity >=0.6.2; import './IUniswapV2Router01.sol'; interface IUniswapV2Router02 is IUniswapV2Router01 { function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountETH); function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountETH); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; /** * @title IVariableDebtToken * @author Lever * @notice Defines the basic interface for a variable debt token. **/ interface IVariableDebtToken is IScaledBalanceToken { /** * @dev Emitted after the mint action * @param from The address performing the mint * @param onBehalfOf The address of the user on which behalf minting has been performed * @param value The amount to be minted * @param index The last index of the reserve **/ event Mint(address indexed from, address indexed onBehalfOf, uint256 value, uint256 index); /** * @dev Mints debt token to the `onBehalfOf` address * @param user The address receiving the borrowed underlying, being the delegatee in case * of credit delegate, or same as `onBehalfOf` otherwise * @param onBehalfOf The address receiving the debt tokens * @param amount The amount of debt being minted * @param index The variable debt index of the reserve * @return `true` if the the previous balance of the user is 0 **/ function mint( address user, address onBehalfOf, uint256 amount, uint256 index ) external returns (bool); /** * @dev Emitted when variable debt is burnt * @param user The user which debt has been burned * @param amount The amount of debt being burned * @param index The index of the user **/ event Burn(address indexed user, uint256 amount, uint256 index); /** * @dev Burns user variable debt * @param user The user which debt is burnt * @param index The variable debt index of the reserve **/ function burn( address user, uint256 amount, uint256 index ) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; interface IWETH { function deposit() external payable; function withdraw(uint256) external; function approve(address guy, uint256 wad) external returns (bool); function transferFrom( address src, address dst, uint256 wad ) external returns (bool); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; interface IWETHGateway { function depositETH(address onBehalfOf) external payable; function withdrawETH(uint256 amount, address onBehalfOf) external; function borrowETH( uint256 amount ) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IERC20} from './IERC20.sol'; import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; interface IXToken is IERC20, IScaledBalanceToken { /** * @dev Emitted after the mint action * @param from The address performing the mint * @param value The amount being * @param index The new liquidity index of the reserve **/ event Mint(address indexed from, uint256 value, uint256 index); /** * @dev Mints `amount` xTokens to `user` * @param user The address receiving the minted tokens * @param amount The amount of tokens getting minted * @param index The new liquidity index of the reserve * @return `true` if the the previous balance of the user was 0 */ function mint( address user, uint256 amount, uint256 index ) external returns (bool); /** * @dev Emitted after xTokens are burned * @param from The owner of the xTokens, getting them burned * @param target The address that will receive the underlying * @param value The amount being burned * @param index The new liquidity index of the reserve **/ event Burn(address indexed from, address indexed target, uint256 value, uint256 index); /** * @dev Emitted during the transfer action * @param from The user whose tokens are being transferred * @param to The recipient * @param value The amount being transferred * @param index The new liquidity index of the reserve **/ event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index); /** * @dev Burns xTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying` * @param user The owner of the xTokens, getting them burned * @param receiverOfUnderlying The address that will receive the underlying * @param amount The amount being burned * @param index The new liquidity index of the reserve **/ function burn( address user, address receiverOfUnderlying, uint256 amount, uint256 index ) external; /** * @dev Mints xTokens to the reserve treasury * @param amount The amount of tokens getting minted * @param index The new liquidity index of the reserve */ function mintToTreasury(uint256 amount, uint256 index) external; /** * @dev Transfers xTokens in the event of a borrow being liquidated, in case the liquidators reclaims the xToken * @param from The address getting liquidated, current owner of the xTokens * @param to The recipient * @param value The amount of tokens getting transferred **/ function transferOnLiquidation( address from, address to, uint256 value ) external; /** * @dev Transfers the underlying asset to `target`. Used by the MarginPool to transfer * assets in borrow(), withdraw() and flashLoan() * @param user The recipient of the xTokens * @param amount The amount getting transferred * @return The amount transferred **/ function transferUnderlyingTo(address user, uint256 amount) external returns (uint256); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {Ownable} from './Ownable.sol'; import {IERC20} from './IERC20.sol'; import {IPriceOracleGetter} from './IPriceOracleGetter.sol'; import {IChainlinkAggregator} from './IChainlinkAggregator.sol'; import {SafeERC20} from './SafeERC20.sol'; /// @title LeverOracle /// @author Lever /// @notice Proxy smart contract to get the price of an asset from a price source, with Chainlink Aggregator /// smart contracts as primary option /// - If the returned price by a Chainlink aggregator is <= 0, the call is forwarded to a fallbackOracle /// - Owned by the Lever governance system, allowed to add sources for assets, replace them /// and change the fallbackOracle contract LeverOracle is IPriceOracleGetter, Ownable { using SafeERC20 for IERC20; event WethSet(address indexed weth); event AssetSourceUpdated(address indexed asset, address indexed source); event FallbackOracleUpdated(address indexed fallbackOracle); mapping(address => IChainlinkAggregator) private assetsSources; IPriceOracleGetter private _fallbackOracle; address public immutable WETH; /// @notice Constructor /// @param assets The addresses of the assets /// @param sources The address of the source of each asset /// @param fallbackOracle The address of the fallback oracle to use if the data of an /// aggregator is not consistent constructor( address[] memory assets, address[] memory sources, address fallbackOracle, address weth ) public { _setFallbackOracle(fallbackOracle); _setAssetsSources(assets, sources); WETH = weth; emit WethSet(weth); } /// @notice External function called by the Lever governance to set or replace sources of assets /// @param assets The addresses of the assets /// @param sources The address of the source of each asset function setAssetSources(address[] calldata assets, address[] calldata sources) external onlyOwner { _setAssetsSources(assets, sources); } /// @notice Sets the fallbackOracle /// - Callable only by the Lever governance /// @param fallbackOracle The address of the fallbackOracle function setFallbackOracle(address fallbackOracle) external onlyOwner { _setFallbackOracle(fallbackOracle); } /// @notice Internal function to set the sources for each asset /// @param assets The addresses of the assets /// @param sources The address of the source of each asset function _setAssetsSources(address[] memory assets, address[] memory sources) internal { require(assets.length == sources.length, 'INCONSISTENT_PARAMS_LENGTH'); for (uint256 i = 0; i < assets.length; i++) { assetsSources[assets[i]] = IChainlinkAggregator(sources[i]); emit AssetSourceUpdated(assets[i], sources[i]); } } /// @notice Internal function to set the fallbackOracle /// @param fallbackOracle The address of the fallbackOracle function _setFallbackOracle(address fallbackOracle) internal { _fallbackOracle = IPriceOracleGetter(fallbackOracle); emit FallbackOracleUpdated(fallbackOracle); } /// @notice Gets an asset price by address /// @param asset The asset address function getAssetPrice(address asset) public view override returns (uint256) { IChainlinkAggregator source = assetsSources[asset]; if (asset == WETH) { return 1 ether; } else if (address(source) == address(0)) { return _fallbackOracle.getAssetPrice(asset); } else { int256 price = IChainlinkAggregator(source).latestAnswer(); if (price > 0) { return uint256(price); } else { return _fallbackOracle.getAssetPrice(asset); } } } /// @notice Gets a list of prices from a list of assets addresses /// @param assets The list of assets addresses function getAssetsPrices(address[] calldata assets) external view returns (uint256[] memory) { uint256[] memory prices = new uint256[](assets.length); for (uint256 i = 0; i < assets.length; i++) { prices[i] = getAssetPrice(assets[i]); } return prices; } /// @notice Gets the address of the source for an asset address /// @param asset The address of the asset /// @return address The address of the source function getSourceOfAsset(address asset) external view returns (address) { return address(assetsSources[asset]); } /// @notice Gets the address of the fallback oracle /// @return address The addres of the fallback oracle function getFallbackOracle() external view returns (address) { return address(_fallbackOracle); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {SafeMath} from "./SafeMath.sol"; import {IERC20} from "./IERC20.sol"; import {SafeERC20} from "./SafeERC20.sol"; import {Address} from "./Address.sol"; import {IMarginPoolAddressesProvider} from "./IMarginPoolAddressesProvider.sol"; import {IXToken} from "./IXToken.sol"; import {IVariableDebtToken} from "./IVariableDebtToken.sol"; import {IPriceOracleGetter} from "./IPriceOracleGetter.sol"; import {IMarginPool} from "./IMarginPool.sol"; import {VersionedInitializable} from "./VersionedInitializable.sol"; import {Helpers} from "./Helpers.sol"; import {Errors} from "./Errors.sol"; import {WadRayMath} from "./WadRayMath.sol"; import {PercentageMath} from "./PercentageMath.sol"; import {ReserveLogic} from "./ReserveLogic.sol"; import {GenericLogic} from "./GenericLogic.sol"; import {ValidationLogic} from "./ValidationLogic.sol"; import {ReserveConfiguration} from "./ReserveConfiguration.sol"; import {UserConfiguration} from "./UserConfiguration.sol"; import {DataTypes} from "./DataTypes.sol"; import {MarginPoolStorage} from "./MarginPoolStorage.sol"; import {IUniswapV2Router02} from "./IUniswapV2Router02.sol"; interface ISwapMining { function swapMint( address account, address input, address output, uint256 amount ) external returns (bool); } /** * @title MarginPool contract * @dev Main point of interaction with an Lever protocol's market * - Users can: * # Deposit * # Withdraw * # Borrow * # Repay * # Liquidate positions * - To be covered by a proxy contract, owned by the MarginPoolAddressesProvider of the specific market * - All admin functions are callable by the MarginPoolConfigurator contract defined also in the * MarginPoolAddressesProvider * @author Lever **/ contract MarginPool is VersionedInitializable, IMarginPool, MarginPoolStorage { using SafeMath for uint256; using WadRayMath for uint256; using PercentageMath for uint256; using SafeERC20 for IERC20; //main configuration parameters uint256 public constant MAX_NUMBER_RESERVES = 128; uint256 public constant MARGINPOOL_REVISION = 0x1; IUniswapV2Router02 public uniswaper; IUniswapV2Router02 public sushiSwaper; address public wethAddress; address public constant inchor = 0x11111112542D85B3EF69AE05771c2dCCff4fAa26; modifier whenNotPaused() { _whenNotPaused(); _; } modifier onlyMarginPoolConfigurator() { _onlyMarginPoolConfigurator(); _; } function _whenNotPaused() internal view { require(!_paused, Errors.MP_IS_PAUSED); } function _onlyMarginPoolConfigurator() internal view { require( _addressesProvider.getMarginPoolConfigurator() == msg.sender, Errors.MP_CALLER_NOT_MARGIN_POOL_CONFIGURATOR ); } function getRevision() internal pure override returns (uint256) { return MARGINPOOL_REVISION; } /** * @dev Function is invoked by the proxy contract when the MarginPool contract is added to the * MarginPoolAddressesProvider of the market. * - Caching the address of the MarginPoolAddressesProvider in order to reduce gas consumption * on subsequent operations * @param provider The address of the MarginPoolAddressesProvider **/ function initialize( IMarginPoolAddressesProvider provider, IUniswapV2Router02 _uniswaper, IUniswapV2Router02 _sushiSwaper, address _weth ) public initializer { _addressesProvider = provider; uniswaper = _uniswaper; sushiSwaper = _sushiSwaper; wethAddress = _weth; } /** * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying xTokens. * - E.g. User deposits 100 USDC and gets in return 100 xUSDC * @param asset The address of the underlying asset to deposit * @param amount The amount to be deposited * @param onBehalfOf The address that will receive the xTokens, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of xTokens * is a different wallet **/ function deposit( address asset, uint256 amount, address onBehalfOf ) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; ValidationLogic.validateDeposit(reserve, amount); address xToken = reserve.xTokenAddress; reserve.updateState(); reserve.updateInterestRates(asset, xToken, amount, 0); IERC20(asset).safeTransferFrom(msg.sender, xToken, amount); _depositLogic(asset, amount, onBehalfOf, xToken, reserve); } function reDeposit( address asset, uint256 amount, address onBehalfOf ) internal whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; ValidationLogic.validateDeposit(reserve, amount); address xToken = reserve.xTokenAddress; reserve.updateState(); reserve.updateInterestRates(asset, xToken, amount, 0); IERC20(asset).safeTransfer(xToken, amount); _depositLogic(asset, amount, onBehalfOf, xToken, reserve); } function _depositLogic( address asset, uint256 amount, address onBehalfOf, address xToken, DataTypes.ReserveData storage reserve ) internal { uint256 variableDebt = Helpers.getUserCurrentDebt(onBehalfOf, reserve); if (variableDebt > 0) { uint256 paybackAmount = variableDebt; if (amount < paybackAmount) { paybackAmount = amount; } IVariableDebtToken(reserve.variableDebtTokenAddress).burn( onBehalfOf, paybackAmount, reserve.variableBorrowIndex ); emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); if (variableDebt == paybackAmount) { _usersConfig[onBehalfOf].setBorrowing(reserve.id, false); } if (amount > paybackAmount) { bool isFirstDeposit = IXToken(xToken).mint( onBehalfOf, amount.sub(paybackAmount), reserve.liquidityIndex ); if (isFirstDeposit) { _usersConfig[onBehalfOf].setUsingAsCollateral( reserve.id, true ); emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf); } emit Deposit( asset, msg.sender, onBehalfOf, amount.sub(paybackAmount) ); } } else { bool isFirstDeposit = IXToken(xToken).mint( onBehalfOf, amount, reserve.liquidityIndex ); if (isFirstDeposit) { _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf); } emit Deposit(asset, msg.sender, onBehalfOf, amount); } } /** * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent xTokens owned * E.g. User has 100 xUSDC, calls withdraw() and receives 100 USDC, burning the 100 xUSDC * @param asset The address of the underlying asset to withdraw * @param amount The underlying amount to be withdrawn * - Send the value type(uint256).max in order to withdraw the whole xToken balance * @param to Address that will receive the underlying, same as msg.sender if the user * wants to receive it on his own wallet, or a different address if the beneficiary is a * different wallet * @return The final amount withdrawn **/ function withdraw( address asset, uint256 amount, address to ) external override whenNotPaused returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; address xToken = reserve.xTokenAddress; uint256 userBalance = IXToken(xToken).balanceOf(msg.sender); uint256 amountToWithdraw = amount; if (amount == type(uint256).max) { amountToWithdraw = userBalance; } ValidationLogic.validateWithdraw( asset, amountToWithdraw, userBalance, _reserves, _usersConfig[msg.sender], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); reserve.updateState(); reserve.updateInterestRates(asset, xToken, 0, amountToWithdraw); if (amountToWithdraw == userBalance) { _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false); emit ReserveUsedAsCollateralDisabled(asset, msg.sender); } IXToken(xToken).burn( msg.sender, to, amountToWithdraw, reserve.liquidityIndex ); emit Withdraw(asset, msg.sender, to, amountToWithdraw); return amountToWithdraw; } /** * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower * already deposited enough collateral, or he was given enough allowance by a credit delegator on the * corresponding debt token ( VariableDebtToken) * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet * and 100 variable debt tokens * @param asset The address of the underlying asset to borrow * @param amount The amount to be borrowed * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator * if he has been given credit delegation allowance **/ function borrow( address asset, uint256 amount, address onBehalfOf ) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; _executeBorrow( ExecuteBorrowParams( asset, msg.sender, onBehalfOf, amount, reserve.xTokenAddress, true ) ); } function swapTokensForTokens( uint256 amountIn, uint256 amountOut, address[] calldata path, bool isExactIn, bool isUni ) external override whenNotPaused { _beforeSwap(path[0], amountIn); IUniswapV2Router02 swaper = isUni ? uniswaper : sushiSwaper; // Approves the transfer for the swap. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. IERC20(path[0]).safeApprove(address(swaper), 0); IERC20(path[0]).safeApprove(address(swaper), amountIn); uint256[] memory awards; if (isExactIn) { awards = swaper.swapExactTokensForTokens( amountIn, amountOut, path, address(this), block.timestamp ); } else { awards = swaper.swapTokensForExactTokens( amountOut, amountIn, path, address(this), block.timestamp ); } reDeposit(path[path.length - 1], awards[awards.length - 1], msg.sender); if (amountIn > awards[0]) { reDeposit(path[0], amountIn.sub(awards[0]), msg.sender); } ValidationLogic.validateSwap( msg.sender, _reserves, _usersConfig[msg.sender], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); ISwapMining(_addressesProvider.getSwapMiner()).swapMint( msg.sender, path[0], path[path.length - 1], awards[awards.length - 1] ); emit Swap( msg.sender, path[0], path[path.length - 1], awards[0], awards[awards.length - 1] ); } function swapTokensForClosePosition( uint256 amountIn, uint256 amountOut, address[] calldata path, bool isExactIn, bool isUni ) external override whenNotPaused { _beforeClose(path[0], amountIn); IUniswapV2Router02 swaper = isUni ? uniswaper : sushiSwaper; // Approves the transfer for the swap. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. IERC20(path[0]).safeApprove(address(swaper), 0); IERC20(path[0]).safeApprove(address(swaper), amountIn); uint256[] memory awards; if (isExactIn) { awards = swaper.swapExactTokensForTokens( amountIn, amountOut, path, address(this), block.timestamp ); } else { awards = swaper.swapTokensForExactTokens( amountOut, amountIn, path, address(this), block.timestamp ); } reDeposit(path[path.length - 1], awards[awards.length - 1], msg.sender); if (amountIn > awards[0]) { reDeposit(path[0], amountIn.sub(awards[0]), msg.sender); } ValidationLogic.validateSwap( msg.sender, _reserves, _usersConfig[msg.sender], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); ISwapMining(_addressesProvider.getSwapMiner()).swapMint( msg.sender, path[0], path[path.length - 1], awards[awards.length - 1] ); emit Swap( msg.sender, path[0], path[path.length - 1], awards[0], awards[awards.length - 1] ); } function swapWithAggregation( address _reserve, uint256 amount, address _reserveTo, bytes memory codes, uint256 gas, uint8 swapType ) external { _beforeSwap(_reserve, amount); IERC20(_reserve).safeApprove(inchor, 0); IERC20(_reserve).safeApprove(inchor, amount); (bool success, bytes memory result) = inchor.call{gas: gas}(codes); require(success, "swap failed"); uint256 award; if (swapType == 1) { award = abi.decode(result, (uint256)); } if (swapType == 2) { (award, ) = abi.decode(result, (uint256, uint256)); } if (swapType == 3) { (award, , ) = abi.decode(result, (uint256, uint256, uint256)); } reDeposit(_reserveTo, award, msg.sender); ValidationLogic.validateSwap( msg.sender, _reserves, _usersConfig[msg.sender], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); ISwapMining(_addressesProvider.getSwapMiner()).swapMint( msg.sender, _reserve, _reserveTo, award ); emit Swap(msg.sender, _reserve, _reserveTo, amount, award); } function closeWithAggregation( address _reserve, uint256 amountIn, address _reserveTo, bytes memory codes, uint256 gas, uint8 swapType ) external { _beforeClose(_reserve, amountIn); IERC20(_reserve).safeApprove(inchor, 0); IERC20(_reserve).safeApprove(inchor, amountIn); (bool success, bytes memory result) = inchor.call{gas: gas}(codes); require(success, "swap failed"); uint256 award; if (swapType == 1) { award = abi.decode(result, (uint256)); } if (swapType == 2) { (award, ) = abi.decode(result, (uint256, uint256)); } if (swapType == 3) { (award, , ) = abi.decode(result, (uint256, uint256, uint256)); } reDeposit(_reserveTo, award, msg.sender); ValidationLogic.validateSwap( msg.sender, _reserves, _usersConfig[msg.sender], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); ISwapMining(_addressesProvider.getSwapMiner()).swapMint( msg.sender, _reserve, _reserveTo, award ); emit Swap(msg.sender, _reserve, _reserveTo, amountIn, award); } function _beforeClose(address _reserve, uint256 amountIn) private { DataTypes.ReserveData storage reserve = _reserves[_reserve]; ValidationLogic.validateDeposit(reserve, amountIn); reserve.updateState(); uint256 userBalance = IXToken(reserve.xTokenAddress).balanceOf(msg.sender); reserve.updateInterestRates( _reserve, reserve.xTokenAddress, 0, amountIn ); IXToken(reserve.xTokenAddress).burn( msg.sender, address(this), amountIn, reserve.liquidityIndex ); if (amountIn == userBalance) { _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false); emit ReserveUsedAsCollateralDisabled(_reserve, msg.sender); } } function _beforeSwap(address _reserve, uint256 amountIn) private { DataTypes.ReserveData storage reserve = _reserves[_reserve]; ValidationLogic.validateDeposit(reserve, amountIn); DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; reserve.updateState(); bool isFirstBorrowing = false; isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress) .mint( msg.sender, msg.sender, amountIn, reserve.variableBorrowIndex ); emit Borrow( _reserve, msg.sender, msg.sender, amountIn, reserve.currentVariableBorrowRate ); if (isFirstBorrowing) { userConfig.setBorrowing(reserve.id, true); } reserve.updateInterestRates( _reserve, reserve.xTokenAddress, 0, amountIn ); IXToken(reserve.xTokenAddress).transferUnderlyingTo( address(this), amountIn ); } /** * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned * - E.g. User repays 100 USDC, burning 100 variable debt tokens of the `onBehalfOf` address * @param asset The address of the borrowed underlying asset previously borrowed * @param amount The amount to repay * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the * user calling the function if he wants to reduce/remove his own debt, or the address of any other * other borrower whose debt should be removed * @return The final amount repaid **/ function repay( address asset, uint256 amount, address onBehalfOf ) external override whenNotPaused returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; uint256 variableDebt = Helpers.getUserCurrentDebt(onBehalfOf, reserve); address xToken = reserve.xTokenAddress; uint256 userBalance = IERC20(xToken).balanceOf(msg.sender); ValidationLogic.validateRepay( reserve, amount, onBehalfOf, variableDebt, userBalance ); uint256 paybackAmount = variableDebt; if (amount < paybackAmount) { paybackAmount = amount; } reserve.updateState(); IVariableDebtToken(reserve.variableDebtTokenAddress).burn( onBehalfOf, paybackAmount, reserve.variableBorrowIndex ); reserve.updateInterestRates(asset, xToken, 0, 0); if (variableDebt.sub(paybackAmount) == 0) { _usersConfig[onBehalfOf].setBorrowing(reserve.id, false); } if (paybackAmount == userBalance) { _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false); emit ReserveUsedAsCollateralDisabled(asset, msg.sender); } IXToken(xToken).burn( msg.sender, xToken, paybackAmount, reserve.liquidityIndex ); emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); return paybackAmount; } /** * @dev Allows depositors to enable/disable a specific deposited asset as collateral * @param asset The address of the underlying asset deposited * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise **/ function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; ValidationLogic.validateSetUseReserveAsCollateral( reserve, asset, useAsCollateral, _reserves, _usersConfig[msg.sender], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); _usersConfig[msg.sender].setUsingAsCollateral( reserve.id, useAsCollateral ); if (useAsCollateral) { emit ReserveUsedAsCollateralEnabled(asset, msg.sender); } else { emit ReserveUsedAsCollateralDisabled(asset, msg.sender); } } struct LiquidationCallLocalVars { uint256 variableDebt; uint256 userBalance; uint256 healthFactor; uint256 maxCollateralToLiquidate; uint256 collateralToSell; uint256 liquidatorPreviousXTokenBalance; } /** * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param user The address of the borrower getting liquidated * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover **/ function liquidationCall( address collateralAsset, address debtAsset, address user, uint256 debtToCover ) external override whenNotPaused { LiquidationCallLocalVars memory vars; DataTypes.ReserveData storage collateralReserve = _reserves[collateralAsset]; DataTypes.ReserveData storage debtReserve = _reserves[debtAsset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[user]; vars.variableDebt = Helpers.getUserCurrentDebt(user, debtReserve).div( 2 ); (, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData( user, _reserves, _usersConfig[user], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); ValidationLogic.validateLiquidation( collateralReserve, debtReserve, userConfig, vars.healthFactor, vars.variableDebt ); vars.variableDebt = vars.variableDebt > debtToCover ? debtToCover : vars.variableDebt; vars.userBalance = IERC20(collateralReserve.xTokenAddress).balanceOf( user ); (vars.maxCollateralToLiquidate, vars.collateralToSell) = GenericLogic .calculateAvailableCollateralToLiquidate( collateralReserve, debtReserve, collateralAsset, debtAsset, vars.variableDebt, vars.userBalance, _addressesProvider.getPriceOracle() ); collateralReserve.updateState(); vars.liquidatorPreviousXTokenBalance = IERC20( collateralReserve .xTokenAddress ) .balanceOf(msg.sender); IXToken(collateralReserve.xTokenAddress).transferOnLiquidation( user, msg.sender, (vars.maxCollateralToLiquidate.sub(vars.collateralToSell)) ); if (vars.liquidatorPreviousXTokenBalance == 0) { DataTypes.UserConfigurationMap storage liquidatorConfig = _usersConfig[msg.sender]; liquidatorConfig.setUsingAsCollateral(collateralReserve.id, true); emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); } if (vars.maxCollateralToLiquidate == vars.userBalance) { userConfig.setUsingAsCollateral(collateralReserve.id, false); emit ReserveUsedAsCollateralDisabled(collateralAsset, user); } if(collateralAsset == debtAsset) { IVariableDebtToken(collateralReserve.variableDebtTokenAddress).burn( user, vars.collateralToSell, collateralReserve.variableBorrowIndex ); collateralReserve.updateInterestRates(collateralAsset, collateralReserve.xTokenAddress, 0, 0); IXToken(collateralReserve.xTokenAddress).burn( user, collateralReserve.xTokenAddress, vars.collateralToSell, collateralReserve.liquidityIndex ); emit LiquidationCall( collateralAsset, debtAsset, user, vars.collateralToSell, vars.collateralToSell, msg.sender ); return; } collateralReserve.updateInterestRates( collateralAsset, collateralReserve.xTokenAddress, 0, vars.collateralToSell ); IXToken(collateralReserve.xTokenAddress).burn( user, address(this), vars.collateralToSell, collateralReserve.liquidityIndex ); IERC20(collateralAsset).safeApprove(address(uniswaper), 0); IERC20(collateralAsset).safeApprove( address(uniswaper), vars.collateralToSell ); address[] memory path; if (collateralAsset != wethAddress && debtAsset != wethAddress) { path = new address[](3); path[0] = collateralAsset; path[1] = wethAddress; path[2] = debtAsset; } else { path = new address[](2); path[0] = collateralAsset; path[1] = debtAsset; } uint256[] memory awards = uniswaper.swapExactTokensForTokens( vars.collateralToSell, vars.variableDebt.mul(97).div(100), path, address(this), block.timestamp ); reDeposit(debtAsset, awards[awards.length - 1], user); emit LiquidationCall( collateralAsset, debtAsset, user, awards[awards.length - 1], vars.collateralToSell, msg.sender ); } /** * @dev Returns the state and configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The state of the reserve **/ function getReserveData(address asset) external view override returns (DataTypes.ReserveData memory) { return _reserves[asset]; } /** * @dev Returns the user account data across all the reserves * @param user The address of the user * @return totalCollateralETH the total collateral in ETH of the user * @return totalDebtETH the total debt in ETH of the user * @return availableBorrowsETH the borrowing power left of the user * @return currentLiquidationThreshold the liquidation threshold of the user * @return ltv the loan to value of the user * @return healthFactor the current health factor of the user **/ function getUserAccountData(address user) external view override returns ( uint256 totalCollateralETH, uint256 totalDebtETH, uint256 availableBorrowsETH, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor ) { ( totalCollateralETH, totalDebtETH, ltv, currentLiquidationThreshold, healthFactor ) = GenericLogic.calculateUserAccountData( user, _reserves, _usersConfig[user], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH( totalCollateralETH, totalDebtETH, ltv ); } /** * @dev Returns the configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The configuration of the reserve **/ function getConfiguration(address asset) external view override returns (DataTypes.ReserveConfigurationMap memory) { return _reserves[asset].configuration; } /** * @dev Returns the configuration of the user across all the reserves * @param user The user address * @return The configuration of the user **/ function getUserConfiguration(address user) external view override returns (DataTypes.UserConfigurationMap memory) { return _usersConfig[user]; } /** * @dev Returns the normalized income per unit of asset * @param asset The address of the underlying asset of the reserve * @return The reserve's normalized income */ function getReserveNormalizedIncome(address asset) external view virtual override returns (uint256) { return _reserves[asset].getNormalizedIncome(); } /** * @dev Returns the normalized variable debt per unit of asset * @param asset The address of the underlying asset of the reserve * @return The reserve normalized variable debt */ function getReserveNormalizedVariableDebt(address asset) external view override returns (uint256) { return _reserves[asset].getNormalizedDebt(); } /** * @dev Returns if the MarginPool is paused */ function paused() external view override returns (bool) { return _paused; } /** * @dev Returns the list of the initialized reserves **/ function getReservesList() external view override returns (address[] memory) { address[] memory _activeReserves = new address[](_reservesCount); for (uint256 i = 0; i < _reservesCount; i++) { _activeReserves[i] = _reservesList[i]; } return _activeReserves; } /** * @dev Returns the cached MarginPoolAddressesProvider connected to this contract **/ function getAddressesProvider() external view override returns (IMarginPoolAddressesProvider) { return _addressesProvider; } /** * @dev Validates and finalizes an xToken transfer * - Only callable by the overlying xToken of the `asset` * @param asset The address of the underlying asset of the xToken * @param from The user from which the xTokens are transferred * @param to The user receiving the xTokens * @param amount The amount being transferred/withdrawn * @param balanceFromBefore The xToken balance of the `from` user before the transfer * @param balanceToBefore The xToken balance of the `to` user before the transfer */ function finalizeTransfer( address asset, address from, address to, uint256 amount, uint256 balanceFromBefore, uint256 balanceToBefore ) external override whenNotPaused { require( msg.sender == _reserves[asset].xTokenAddress, Errors.MP_CALLER_MUST_BE_AN_XTOKEN ); ValidationLogic.validateTransfer( from, _reserves, _usersConfig[from], _reservesList, _reservesCount, _addressesProvider.getPriceOracle() ); uint256 reserveId = _reserves[asset].id; if (from != to) { if (balanceFromBefore.sub(amount) == 0) { DataTypes.UserConfigurationMap storage fromConfig = _usersConfig[from]; fromConfig.setUsingAsCollateral(reserveId, false); emit ReserveUsedAsCollateralDisabled(asset, from); } if (balanceToBefore == 0 && amount != 0) { DataTypes.UserConfigurationMap storage toConfig = _usersConfig[to]; toConfig.setUsingAsCollateral(reserveId, true); emit ReserveUsedAsCollateralEnabled(asset, to); } } } /** * @dev Initializes a reserve, activating it, assigning an xToken and debt tokens and an * interest rate strategy * - Only callable by the MarginPoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param xTokenAddress The address of the xToken that will be assigned to the reserve * @param xTokenAddress The address of the VariableDebtToken that will be assigned to the reserve * @param interestRateStrategyAddress The address of the interest rate strategy contract **/ function initReserve( address asset, address xTokenAddress, address variableDebtAddress, address interestRateStrategyAddress ) external override onlyMarginPoolConfigurator { require(Address.isContract(asset), Errors.MP_NOT_CONTRACT); _reserves[asset].init( xTokenAddress, variableDebtAddress, interestRateStrategyAddress ); _addReserveToList(asset); } /** * @dev Updates the address of the interest rate strategy contract * - Only callable by the MarginPoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param rateStrategyAddress The address of the interest rate strategy contract **/ function setReserveInterestRateStrategyAddress( address asset, address rateStrategyAddress ) external override onlyMarginPoolConfigurator { _reserves[asset].interestRateStrategyAddress = rateStrategyAddress; } /** * @dev Sets the configuration bitmap of the reserve as a whole * - Only callable by the MarginPoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param configuration The new configuration bitmap **/ function setConfiguration(address asset, uint256 configuration) external override onlyMarginPoolConfigurator { _reserves[asset].configuration.data = configuration; } /** * @dev Set the _pause state of a reserve * - Only callable by the MarginPoolConfigurator contract * @param val `true` to pause the reserve, `false` to un-pause it */ function setPause(bool val) external override onlyMarginPoolConfigurator { _paused = val; if (_paused) { emit Paused(); } else { emit Unpaused(); } } struct ExecuteBorrowParams { address asset; address user; address onBehalfOf; uint256 amount; address xTokenAddress; bool releaseUnderlying; } function _executeBorrow(ExecuteBorrowParams memory vars) internal { DataTypes.ReserveData storage reserve = _reserves[vars.asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; address oracle = _addressesProvider.getPriceOracle(); uint256 amountInETH = IPriceOracleGetter(oracle) .getAssetPrice(vars.asset) .mul(vars.amount) .div(10**reserve.configuration.getDecimals()); ValidationLogic.validateBorrow( reserve, vars.onBehalfOf, vars.amount, amountInETH, _reserves, userConfig, _reservesList, _reservesCount, oracle ); reserve.updateState(); bool isFirstBorrowing = false; isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress) .mint( vars.user, vars.onBehalfOf, vars.amount, reserve.variableBorrowIndex ); if (isFirstBorrowing) { userConfig.setBorrowing(reserve.id, true); } reserve.updateInterestRates( vars.asset, vars.xTokenAddress, 0, vars.releaseUnderlying ? vars.amount : 0 ); if (vars.releaseUnderlying) { IXToken(vars.xTokenAddress).transferUnderlyingTo( vars.user, vars.amount ); } emit Borrow( vars.asset, vars.user, vars.onBehalfOf, vars.amount, reserve.currentVariableBorrowRate ); } function _addReserveToList(address asset) internal { uint256 reservesCount = _reservesCount; require( reservesCount < MAX_NUMBER_RESERVES, Errors.MP_NO_MORE_RESERVES_ALLOWED ); bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; if (!reserveAlreadyAdded) { _reserves[asset].id = uint8(reservesCount); _reservesList[reservesCount] = asset; _reservesCount = reservesCount + 1; } } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {Ownable} from './Ownable.sol'; import './Address.sol'; // Prettier ignore to prevent buidler flatter bug // prettier-ignore import {InitializableImmutableAdminUpgradeabilityProxy} from './InitializableImmutableAdminUpgradeabilityProxy.sol'; import {IMarginPoolAddressesProvider} from './IMarginPoolAddressesProvider.sol'; // import './BaseUpgradeabilityProxy.sol'; /** * @title MarginPoolAddressesProvider contract * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles * - Acting also as factory of proxies and admin of those, so with right to change its implementations * - Owned by the Lever Governance * @author Lever **/ contract MarginPoolAddressesProvider is Ownable, IMarginPoolAddressesProvider { mapping(bytes32 => address) private _addresses; bytes32 private constant MARGIN_POOL = 'MARGIN_POOL'; bytes32 private constant MARGIN_POOL_CONFIGURATOR = 'MARGIN_POOL_CONFIGURATOR'; bytes32 private constant POOL_ADMIN = 'POOL_ADMIN'; bytes32 private constant EMERGENCY_ADMIN = 'EMERGENCY_ADMIN'; bytes32 private constant PRICE_ORACLE = 'PRICE_ORACLE'; bytes32 private constant LENDING_RATE_ORACLE = 'LENDING_RATE_ORACLE'; bytes32 private constant LEVER_TOKEN = 'LEVER_TOKEN'; bytes32 private constant TREASURY_ADDRESS = 'TREASURY_ADDRESS'; bytes32 private constant REWARDS_DISTRIBUTION = 'REWARDS_DISTRIBUTION'; bytes32 private constant SWAP_MINER = 'SWAP_MINER'; bytes32 private constant ORDER_BOOK = 'ORDER_BOOK'; constructor() public { } /** * @dev General function to update the implementation of a proxy registered with * certain `id`. If there is no proxy registered, it will instantiate one and * set as implementation the `implementationAddress` * IMPORTANT Use this function carefully, only for ids that don't have an explicit * setter function, in order to avoid unexpected consequences * @param id The id * @param implementationAddress The address of the new implementation */ function setAddressAsProxy(bytes32 id, address implementationAddress) external override onlyOwner { _updateImpl(id, implementationAddress); emit AddressSet(id, implementationAddress, true); } /** * @dev Sets an address for an id replacing the address saved in the addresses map * IMPORTANT Use this function carefully, as it will do a hard replacement * @param id The id * @param newAddress The address to set */ function setAddress(bytes32 id, address newAddress) external override onlyOwner { _addresses[id] = newAddress; emit AddressSet(id, newAddress, false); } /** * @dev Returns an address by id * @return The address */ function getAddress(bytes32 id) public view override returns (address) { return _addresses[id]; } /** * @dev Returns the address of the MarginPool proxy * @return The MarginPool proxy address **/ function getMarginPool() external view override returns (address) { return getAddress(MARGIN_POOL); } /** * @dev Updates the implementation of the MarginPool, or creates the proxy * setting the new `pool` implementation on the first time calling it * @param pool The new MarginPool implementation **/ function setMarginPoolImpl(address pool,address UniswapRouter, address SushiswapRouter,address _weth) external override onlyOwner { _updatePoolImpl(MARGIN_POOL, pool, UniswapRouter,SushiswapRouter, _weth); emit MarginPoolUpdated(pool); } /** * @dev Returns the address of the MarginPoolConfigurator proxy * @return The MarginPoolConfigurator proxy address **/ function getMarginPoolConfigurator() external view override returns (address) { return getAddress(MARGIN_POOL_CONFIGURATOR); } /** * @dev Updates the implementation of the MarginPoolConfigurator, or creates the proxy * setting the new `configurator` implementation on the first time calling it * @param configurator The new MarginPoolConfigurator implementation **/ function setMarginPoolConfiguratorImpl(address configurator) external override onlyOwner { _updateImpl(MARGIN_POOL_CONFIGURATOR, configurator); emit MarginPoolConfiguratorUpdated(configurator); } /** * @dev The functions below are getters/setters of addresses that are outside the context * of the protocol hence the upgradable proxy pattern is not used **/ function getPoolAdmin() external view override returns (address) { return getAddress(POOL_ADMIN); } function setPoolAdmin(address admin) external override onlyOwner { _addresses[POOL_ADMIN] = admin; emit ConfigurationAdminUpdated(admin); } function getEmergencyAdmin() external view override returns (address) { return getAddress(EMERGENCY_ADMIN); } function setEmergencyAdmin(address emergencyAdmin) external override onlyOwner { _addresses[EMERGENCY_ADMIN] = emergencyAdmin; emit EmergencyAdminUpdated(emergencyAdmin); } function getPriceOracle() external view override returns (address) { return getAddress(PRICE_ORACLE); } function setPriceOracle(address priceOracle) external override onlyOwner { _addresses[PRICE_ORACLE] = priceOracle; emit PriceOracleUpdated(priceOracle); } function getLeverToken() external view override returns (address) { return getAddress(LEVER_TOKEN); } function setLeverToken(address lever) external override onlyOwner { _addresses[LEVER_TOKEN] = lever; emit LeverTokenUpdated(lever); } function getTreasuryAddress() external view override returns (address) { return getAddress(TREASURY_ADDRESS); } function setTreasuryAddress(address treasuryAddress) external override onlyOwner { _addresses[TREASURY_ADDRESS] = treasuryAddress; emit TreasuryAddressUpdated(treasuryAddress); } function getRewardsDistribution() external view override returns (address) { return getAddress(REWARDS_DISTRIBUTION); } function setRewardsDistribution(address rewardsDistribution) external override onlyOwner { _addresses[REWARDS_DISTRIBUTION] = rewardsDistribution; emit RewardsDistributionUpdated(rewardsDistribution); } /** * @dev Returns the address of the OrderBook proxy * @return The OrderBook proxy address **/ function getOrderBook() external view override returns (address) { return getAddress(ORDER_BOOK); } /** * @dev Updates the implementation of the OrderBook, or creates the proxy * setting the new `pool` implementation on the first time calling it * @param orderBook The new OrderBook implementation **/ function setOrderBookImpl(address orderBook, address UniswapRouter, address _weth) external override onlyOwner { _updateImpl(ORDER_BOOK, orderBook, UniswapRouter, _weth); emit OrderBookUpdated(orderBook); } /** * @dev Returns the address of the SwapMiner proxy * @return The SwapMiner proxy address **/ function getSwapMiner() external view override returns (address) { return getAddress(SWAP_MINER); } /** * @dev Updates the implementation of the SwapMiner, or creates the proxy * setting the new `pool` implementation on the first time calling it * @param swapMiner The new SwapMiner implementation **/ function setSwapMinerImpl(address swapMiner, address UniswapRouter, address _uniswapLevPairToken, address LeverUsdOracle) external override onlyOwner { _updateSwapMinerImpl(SWAP_MINER, swapMiner, UniswapRouter, _uniswapLevPairToken, LeverUsdOracle); emit SwapMinerUpdated(swapMiner); } /** * @dev Internal function to update the implementation of a specific proxied component of the protocol * - If there is no proxy registered in the given `id`, it creates the proxy setting `newAdress` * as implementation and calls the initialize() function on the proxy * - If there is already a proxy registered, it just updates the implementation to `newAddress` and * calls the initialize() function via upgradeToAndCall() in the proxy * @param id The id of the proxy to be updated * @param newAddress The address of the new implementation **/ function _updateImpl(bytes32 id, address newAddress) internal { address payable proxyAddress = payable(_addresses[id]); InitializableImmutableAdminUpgradeabilityProxy proxy = InitializableImmutableAdminUpgradeabilityProxy(proxyAddress); bytes memory params = abi.encodeWithSignature('initialize(address)', address(this)); if (proxyAddress == address(0)) { proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this)); proxy.initialize(newAddress, params); _addresses[id] = address(proxy); emit ProxyCreated(id, address(proxy)); } else { proxy.upgradeToAndCall(newAddress, params); } } /** * @dev Internal function to update the implementation of a specific proxied component of the protocol * - If there is no proxy registered in the given `id`, it creates the proxy setting `newAdress` * as implementation and calls the initialize() function on the proxy * - If there is already a proxy registered, it just updates the implementation to `newAddress` and * calls the initialize() function via upgradeToAndCall() in the proxy * @param id The id of the proxy to be updated * @param newAddress The address of the new implementation **/ function _updateImpl(bytes32 id, address newAddress, address UniswapRouter,address _weth) internal { address payable proxyAddress = payable(_addresses[id]); InitializableImmutableAdminUpgradeabilityProxy proxy = InitializableImmutableAdminUpgradeabilityProxy(proxyAddress); bytes memory params = abi.encodeWithSignature('initialize(address,address,address)', address(this), UniswapRouter,_weth); if (proxyAddress == address(0)) { proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this)); proxy.initialize(newAddress, params); _addresses[id] = address(proxy); emit ProxyCreated(id, address(proxy)); } else { proxy.upgradeToAndCall(newAddress, params); } } /** * @dev Internal function to update the implementation of a specific proxied component of the protocol * - If there is no proxy registered in the given `id`, it creates the proxy setting `newAdress` * as implementation and calls the initialize() function on the proxy * - If there is already a proxy registered, it just updates the implementation to `newAddress` and * calls the initialize() function via upgradeToAndCall() in the proxy * @param id The id of the proxy to be updated * @param newAddress The address of the new implementation **/ function _updatePoolImpl(bytes32 id, address newAddress, address UniswapRouter, address SushiswapRouter,address _weth) internal { address payable proxyAddress = payable(_addresses[id]); InitializableImmutableAdminUpgradeabilityProxy proxy = InitializableImmutableAdminUpgradeabilityProxy(proxyAddress); bytes memory params = abi.encodeWithSignature('initialize(address,address,address,address)', address(this), UniswapRouter,SushiswapRouter, _weth); if (proxyAddress == address(0)) { proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this)); proxy.initialize(newAddress, params); _addresses[id] = address(proxy); emit ProxyCreated(id, address(proxy)); } else { proxy.upgradeToAndCall(newAddress, params); } } function _updateSwapMinerImpl(bytes32 id, address newAddress, address UniswapRouter,address _uniswapLevPairToken,address LeverUsdOracle) internal { address payable proxyAddress = payable(_addresses[id]); InitializableImmutableAdminUpgradeabilityProxy proxy = InitializableImmutableAdminUpgradeabilityProxy(proxyAddress); bytes memory params = abi.encodeWithSignature('initialize(address,address,address,address)', address(this), UniswapRouter,_uniswapLevPairToken,LeverUsdOracle); if (proxyAddress == address(0)) { proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this)); proxy.initialize(newAddress, params); _addresses[id] = address(proxy); emit ProxyCreated(id, address(proxy)); } else { proxy.upgradeToAndCall(newAddress, params); } } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {SafeMath} from './SafeMath.sol'; import {VersionedInitializable} from './VersionedInitializable.sol'; import {ReserveConfiguration} from './ReserveConfiguration.sol'; import {IMarginPoolAddressesProvider} from './IMarginPoolAddressesProvider.sol'; import {IMarginPool} from './IMarginPool.sol'; import {ITokenConfiguration} from './ITokenConfiguration.sol'; import {IERC20Detailed} from './IERC20Detailed.sol'; import {Errors} from './Errors.sol'; import {PercentageMath} from './PercentageMath.sol'; import {DataTypes} from './DataTypes.sol'; /** * @title MarginPoolConfigurator contract * @author Lever * @dev Implements the configuration methods for the Lever protocol **/ contract MarginPoolConfigurator is VersionedInitializable { using SafeMath for uint256; using PercentageMath for uint256; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; /** * @dev Emitted when a reserve is initialized. * @param asset The address of the underlying asset of the reserve * @param xToken The address of the associated xToken contract * @param variableDebtToken The address of the associated variable rate debt token * @param interestRateStrategyAddress The address of the interest rate strategy for the reserve **/ event ReserveInitialized( address indexed asset, address indexed xToken, address variableDebtToken, address interestRateStrategyAddress ); /** * @dev Emitted when borrowing is enabled on a reserve * @param asset The address of the underlying asset of the reserve * @param rateEnabled false otherwise **/ /** * @dev Emitted when borrowing is disabled on a reserve * @param asset The address of the underlying asset of the reserve **/ event BorrowingDisabledOnReserve(address indexed asset); /** * @dev Emitted when the collateralization risk parameters for the specified asset are updated. * @param asset The address of the underlying asset 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 CollateralConfigurationChanged( address indexed asset, uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus ); /** * @dev Emitted when a reserve is activated * @param asset The address of the underlying asset of the reserve **/ event ReserveActivated(address indexed asset); /** * @dev Emitted when a reserve is deactivated * @param asset The address of the underlying asset of the reserve **/ event ReserveDeactivated(address indexed asset); /** * @dev Emitted when a reserve is frozen * @param asset The address of the underlying asset of the reserve **/ event ReserveFrozen(address indexed asset); /** * @dev Emitted when a reserve is unfrozen * @param asset The address of the underlying asset of the reserve **/ event ReserveUnfrozen(address indexed asset); /** * @dev Emitted when a reserve factor is updated * @param asset The address of the underlying asset of the reserve * @param factor The new reserve factor **/ event ReserveFactorChanged(address indexed asset, uint256 factor); /** * @dev Emitted when the reserve decimals are updated * @param asset The address of the underlying asset of the reserve * @param decimals The new decimals **/ event ReserveDecimalsChanged(address indexed asset, uint256 decimals); /** * @dev Emitted when a reserve interest strategy contract is updated * @param asset The address of the underlying asset of the reserve * @param strategy The new address of the interest strategy contract **/ event ReserveInterestRateStrategyChanged(address indexed asset, address strategy); /** * @dev Emitted when an xToken implementation is upgraded * @param asset The address of the underlying asset of the reserve * @param proxy The xToken proxy address * @param implementation The new xToken implementation **/ event XTokenUpgraded( address indexed asset, address indexed proxy, address indexed implementation ); /** * @dev Emitted when the implementation of a variable debt token is upgraded * @param asset The address of the underlying asset of the reserve * @param proxy The variable debt token proxy address * @param implementation The new xToken implementation **/ event VariableDebtTokenUpgraded( address indexed asset, address indexed proxy, address indexed implementation ); IMarginPoolAddressesProvider public addressesProvider; IMarginPool public pool; modifier onlyPoolAdmin { require(addressesProvider.getPoolAdmin() == msg.sender, Errors.CALLER_NOT_POOL_ADMIN); _; } modifier onlyEmergencyAdmin { require( addressesProvider.getEmergencyAdmin() == msg.sender, Errors.MPC_CALLER_NOT_EMERGENCY_ADMIN ); _; } uint256 internal constant CONFIGURATOR_REVISION = 0x1; function getRevision() internal pure override returns (uint256) { return CONFIGURATOR_REVISION; } function initialize(IMarginPoolAddressesProvider provider) public initializer { addressesProvider = provider; pool = IMarginPool(addressesProvider.getMarginPool()); } /** * @dev Initializes a reserve * @param xTokenImpl The address of the xToken contract implementation * @param variableDebtTokenImpl The address of the variable debt token contract * @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 xTokenImpl, address variableDebtTokenImpl, uint8 underlyingAssetDecimals, address interestRateStrategyAddress ) public onlyPoolAdmin { address asset = ITokenConfiguration(xTokenImpl).UNDERLYING_ASSET_ADDRESS(); require( address(pool) == ITokenConfiguration(xTokenImpl).POOL(), Errors.MPC_INVALID_XTOKEN_POOL_ADDRESS ); require( address(pool) == ITokenConfiguration(variableDebtTokenImpl).POOL(), Errors.MPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS ); require( asset == ITokenConfiguration(variableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(), Errors.MPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS ); pool.initReserve( asset, xTokenImpl, variableDebtTokenImpl, interestRateStrategyAddress ); DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setDecimals(underlyingAssetDecimals); currentConfig.setActive(true); currentConfig.setFrozen(false); pool.setConfiguration(asset, currentConfig.data); emit ReserveInitialized( asset, xTokenImpl, variableDebtTokenImpl, interestRateStrategyAddress ); } /** * @dev Enables borrowing on a reserve * @param asset The address of the underlying asset of the reserve **/ function enableBorrowingOnReserve(address asset) external onlyPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setBorrowingEnabled(true); pool.setConfiguration(asset, currentConfig.data); } /** * @dev Disables borrowing on a reserve * @param asset The address of the underlying asset of the reserve **/ function disableBorrowingOnReserve(address asset) external onlyPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setBorrowingEnabled(false); pool.setConfiguration(asset, currentConfig.data); emit BorrowingDisabledOnReserve(asset); } /** * @dev Configures the reserve collateralization parameters * all the values are expressed in percentages with two decimals of precision. A valid value is 10000, which means 100.00% * @param asset The address of the underlying asset 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. The values is always above 100%. A value of 105% * means the liquidator will receive a 5% bonus **/ function configureReserveAsCollateral( address asset, uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus ) external onlyPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); //validation of the parameters: the LTV can //only be lower or equal than the liquidation threshold //(otherwise a loan against the asset would cause instantaneous liquidation) require(ltv <= liquidationThreshold, Errors.MPC_INVALID_CONFIGURATION); if (liquidationThreshold != 0) { //liquidation bonus must be bigger than 100.00%, otherwise the liquidator would receive less //collateral than needed to cover the debt require( liquidationBonus > PercentageMath.PERCENTAGE_FACTOR, Errors.MPC_INVALID_CONFIGURATION ); //if threshold * bonus is less than PERCENTAGE_FACTOR, it's guaranteed that at the moment //a loan is taken there is enough collateral available to cover the liquidation bonus require( liquidationThreshold.percentMul(liquidationBonus) <= PercentageMath.PERCENTAGE_FACTOR, Errors.MPC_INVALID_CONFIGURATION ); } else { require(liquidationBonus == 0, Errors.MPC_INVALID_CONFIGURATION); //if the liquidation threshold is being set to 0, // the reserve is being disabled as collateral. To do so, //we need to ensure no liquidity is deposited _checkNoLiquidity(asset); } currentConfig.setLtv(ltv); currentConfig.setLiquidationThreshold(liquidationThreshold); currentConfig.setLiquidationBonus(liquidationBonus); pool.setConfiguration(asset, currentConfig.data); emit CollateralConfigurationChanged(asset, ltv, liquidationThreshold, liquidationBonus); } /** * @dev Activates a reserve * @param asset The address of the underlying asset of the reserve **/ function activateReserve(address asset) external onlyPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setActive(true); pool.setConfiguration(asset, currentConfig.data); emit ReserveActivated(asset); } /** * @dev Deactivates a reserve * @param asset The address of the underlying asset of the reserve **/ function deactivateReserve(address asset) external onlyPoolAdmin { _checkNoLiquidity(asset); DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setActive(false); pool.setConfiguration(asset, currentConfig.data); emit ReserveDeactivated(asset); } /** * @dev Freezes a reserve. A frozen reserve doesn't allow any new deposit, borrow or rate swap * but allows repayments, liquidations, rate rebalances and withdrawals * @param asset The address of the underlying asset of the reserve **/ function freezeReserve(address asset) external onlyPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setFrozen(true); pool.setConfiguration(asset, currentConfig.data); emit ReserveFrozen(asset); } /** * @dev Unfreezes a reserve * @param asset The address of the underlying asset of the reserve **/ function unfreezeReserve(address asset) external onlyPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setFrozen(false); pool.setConfiguration(asset, currentConfig.data); emit ReserveUnfrozen(asset); } /** * @dev Updates the reserve factor of a reserve * @param asset The address of the underlying asset of the reserve * @param reserveFactor The new reserve factor of the reserve **/ function setReserveFactor(address asset, uint256 reserveFactor) external onlyPoolAdmin { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setReserveFactor(reserveFactor); pool.setConfiguration(asset, currentConfig.data); emit ReserveFactorChanged(asset, reserveFactor); } /** * @dev Sets the interest rate strategy of a reserve * @param asset The address of the underlying asset of the reserve * @param rateStrategyAddress The new address of the interest strategy contract **/ function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress) external onlyPoolAdmin { pool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress); emit ReserveInterestRateStrategyChanged(asset, rateStrategyAddress); } /** * @dev pauses or unpauses all the actions of the protocol, including xToken transfers * @param val true if protocol needs to be paused, false otherwise **/ function setPoolPause(bool val) external onlyEmergencyAdmin { pool.setPause(val); } function _checkNoLiquidity(address asset) internal view { DataTypes.ReserveData memory reserveData = pool.getReserveData(asset); uint256 availableLiquidity = IERC20Detailed(asset).balanceOf(reserveData.xTokenAddress); require( availableLiquidity == 0 && reserveData.currentLiquidityRate == 0, Errors.MPC_RESERVE_LIQUIDITY_NOT_0 ); } }
pragma solidity 0.6.12; // SPDX-License-Identifier: agpl-3.0 import {UserConfiguration} from './UserConfiguration.sol'; import {ReserveConfiguration} from './ReserveConfiguration.sol'; import {ReserveLogic} from './ReserveLogic.sol'; import {IMarginPoolAddressesProvider} from './IMarginPoolAddressesProvider.sol'; import {DataTypes} from './DataTypes.sol'; contract MarginPoolStorage { using ReserveLogic for DataTypes.ReserveData; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using UserConfiguration for DataTypes.UserConfigurationMap; IMarginPoolAddressesProvider internal _addressesProvider; mapping(address => DataTypes.ReserveData) internal _reserves; mapping(address => DataTypes.UserConfigurationMap) internal _usersConfig; // the list of the available reserves, structured as a mapping for gas savings reasons mapping(uint256 => address) internal _reservesList; uint256 internal _reservesCount; bool internal _paused; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {SafeMath} from './SafeMath.sol'; import {WadRayMath} from './WadRayMath.sol'; library MathUtils { using SafeMath for uint256; using WadRayMath for uint256; /// @dev Ignoring leap years uint256 internal constant SECONDS_PER_YEAR = 365 days; /** * @dev Function to calculate the interest accumulated 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)); return (rate.mul(timeDifference) / SECONDS_PER_YEAR).add(WadRayMath.ray()); } /** * @dev Function to calculate the interest using a compounded interest rate formula * To avoid expensive exponentiation, the calculation is performed using a binomial approximation: * * (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3... * * The approximation slightly underpays liquidity providers and undercharges borrowers, with the advantage of great gas cost reductions * The whitepaper contains reference to the approximation and a table showing the margin of error per different time periods * * @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, uint256 currentTimestamp ) internal pure returns (uint256) { //solium-disable-next-line uint256 exp = currentTimestamp.sub(uint256(lastUpdateTimestamp)); if (exp == 0) { return WadRayMath.ray(); } uint256 expMinusOne = exp - 1; uint256 expMinusTwo = exp > 2 ? exp - 2 : 0; uint256 ratePerSecond = rate / SECONDS_PER_YEAR; uint256 basePowerTwo = ratePerSecond.rayMul(ratePerSecond); uint256 basePowerThree = basePowerTwo.rayMul(ratePerSecond); uint256 secondTerm = exp.mul(expMinusOne).mul(basePowerTwo) / 2; uint256 thirdTerm = exp.mul(expMinusOne).mul(expMinusTwo).mul(basePowerThree) / 6; return WadRayMath.ray().add(ratePerSecond.mul(exp)).add(secondTerm).add(thirdTerm); } /** * @dev Calculates the compounded interest between the timestamp of the last update and the current block timestamp * @param rate The interest rate (in ray) * @param lastUpdateTimestamp The timestamp from which the interest accumulation needs to be calculated **/ function calculateCompoundedInterest(uint256 rate, uint40 lastUpdateTimestamp) internal view returns (uint256) { return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; contract Migrations { address public owner = msg.sender; uint public last_completed_migration; modifier restricted() { require( msg.sender == owner, "This function is restricted to the contract's owner" ); _; } function setCompleted(uint completed) public restricted { last_completed_migration = completed; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.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. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * 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(_owner == _msgSender(), 'Ownable: caller is not the 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 virtual 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 virtual onlyOwner { require(newOwner != address(0), 'Ownable: new owner is the zero address'); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {Errors} from './Errors.sol'; /** * @title PercentageMath library * @author Lever * @notice Provides functions to perform percentage calculations * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR * @dev Operations are rounded half up **/ library PercentageMath { uint256 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals uint256 constant HALF_PERCENT = PERCENTAGE_FACTOR / 2; /** * @dev Executes a percentage multiplication * @param value The value of which the percentage needs to be calculated * @param percentage The percentage of the value to be calculated * @return The percentage of value **/ function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256) { if (value == 0 || percentage == 0) { return 0; } require( value <= (type(uint256).max - HALF_PERCENT) / percentage, Errors.MATH_MULTIPLICATION_OVERFLOW ); return (value * percentage + HALF_PERCENT) / PERCENTAGE_FACTOR; } /** * @dev Executes a percentage division * @param value The value of which the percentage needs to be calculated * @param percentage The percentage of the value to be calculated * @return The value divided the percentage **/ function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256) { require(percentage != 0, Errors.MATH_DIVISION_BY_ZERO); uint256 halfPercentage = percentage / 2; require( value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR, Errors.MATH_MULTIPLICATION_OVERFLOW ); return (value * PERCENTAGE_FACTOR + halfPercentage) / percentage; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IPriceOracle} from './IPriceOracle.sol'; contract PriceOracle is IPriceOracle { mapping(address => uint256) prices; uint256 ethPriceUsd; event AssetPriceUpdated(address _asset, uint256 _price, uint256 timestamp); event EthPriceUpdated(uint256 _price, uint256 timestamp); function getAssetPrice(address _asset) external view override returns (uint256) { return prices[_asset]; } function setAssetPrice(address _asset, uint256 _price) external override { prices[_asset] = _price; emit AssetPriceUpdated(_asset, _price, block.timestamp); } function getEthUsdPrice() external view returns (uint256) { return ethPriceUsd; } function setEthUsdPrice(uint256 _price) external { ethPriceUsd = _price; emit EthPriceUpdated(_price, block.timestamp); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.6.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. */ abstract contract Proxy { /** * @dev Fallback function. * Implemented entirely in `_fallback`. */ fallback() external payable { _fallback(); } /** * @return The Address of the implementation. */ function _implementation() internal view virtual 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 virtual {} /** * @dev fallback implementation. * Extracted to enable manual triggering. */ function _fallback() internal { _willFallback(); _delegate(_implementation()); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {Errors} from './Errors.sol'; import {DataTypes} from './DataTypes.sol'; /** * @title ReserveConfiguration library * @author Lever * @notice Implements the bitmap logic to handle the reserve configuration */ library ReserveConfiguration { uint256 constant LTV_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore uint256 constant LIQUIDATION_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore uint256 constant LIQUIDATION_BONUS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore uint256 constant DECIMALS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore uint256 constant ACTIVE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore uint256 constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore uint256 constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore uint256 constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; uint256 constant LIQUIDATION_BONUS_START_BIT_POSITION = 32; uint256 constant RESERVE_DECIMALS_START_BIT_POSITION = 48; uint256 constant IS_ACTIVE_START_BIT_POSITION = 56; uint256 constant IS_FROZEN_START_BIT_POSITION = 57; uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58; uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64; uint256 constant MAX_VALID_LTV = 65535; uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; uint256 constant MAX_VALID_LIQUIDATION_BONUS = 65535; uint256 constant MAX_VALID_DECIMALS = 255; uint256 constant MAX_VALID_RESERVE_FACTOR = 65535; /** * @dev Sets the Loan to Value of the reserve * @param self The reserve configuration * @param ltv the new ltv **/ function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure { require(ltv <= MAX_VALID_LTV, Errors.RC_INVALID_LTV); self.data = (self.data & LTV_MASK) | ltv; } /** * @dev Gets the Loan to Value of the reserve * @param self The reserve configuration * @return The loan to value **/ function getLtv(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return self.data & ~LTV_MASK; } /** * @dev Sets the liquidation threshold of the reserve * @param self The reserve configuration * @param threshold The new liquidation threshold **/ function setLiquidationThreshold(DataTypes.ReserveConfigurationMap memory self, uint256 threshold) internal pure { require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.RC_INVALID_LIQ_THRESHOLD); self.data = (self.data & LIQUIDATION_THRESHOLD_MASK) | (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION); } /** * @dev Gets the liquidation threshold of the reserve * @param self The reserve configuration * @return The liquidation threshold **/ function getLiquidationThreshold(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; } /** * @dev Sets the liquidation bonus of the reserve * @param self The reserve configuration * @param bonus The new liquidation bonus **/ function setLiquidationBonus(DataTypes.ReserveConfigurationMap memory self, uint256 bonus) internal pure { require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.RC_INVALID_LIQ_BONUS); self.data = (self.data & LIQUIDATION_BONUS_MASK) | (bonus << LIQUIDATION_BONUS_START_BIT_POSITION); } /** * @dev Gets the liquidation bonus of the reserve * @param self The reserve configuration * @return The liquidation bonus **/ function getLiquidationBonus(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION; } /** * @dev Sets the decimals of the underlying asset of the reserve * @param self The reserve configuration * @param decimals The decimals **/ function setDecimals(DataTypes.ReserveConfigurationMap memory self, uint256 decimals) internal pure { require(decimals <= MAX_VALID_DECIMALS, Errors.RC_INVALID_DECIMALS); self.data = (self.data & DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION); } /** * @dev Gets the decimals of the underlying asset of the reserve * @param self The reserve configuration * @return The decimals of the asset **/ function getDecimals(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; } /** * @dev Sets the active state of the reserve * @param self The reserve configuration * @param active The active state **/ function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure { self.data = (self.data & ACTIVE_MASK) | (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION); } /** * @dev Gets the active state of the reserve * @param self The reserve configuration * @return The active state **/ function getActive(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { return (self.data & ~ACTIVE_MASK) != 0; } /** * @dev Sets the frozen state of the reserve * @param self The reserve configuration * @param frozen The frozen state **/ function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure { self.data = (self.data & FROZEN_MASK) | (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION); } /** * @dev Gets the frozen state of the reserve * @param self The reserve configuration * @return The frozen state **/ function getFrozen(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { return (self.data & ~FROZEN_MASK) != 0; } /** * @dev Enables or disables borrowing on the reserve * @param self The reserve configuration * @param enabled True if the borrowing needs to be enabled, false otherwise **/ function setBorrowingEnabled(DataTypes.ReserveConfigurationMap memory self, bool enabled) internal pure { self.data = (self.data & BORROWING_MASK) | (uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION); } /** * @dev Gets the borrowing state of the reserve * @param self The reserve configuration * @return The borrowing state **/ function getBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { return (self.data & ~BORROWING_MASK) != 0; } /** * @dev Sets the reserve factor of the reserve * @param self The reserve configuration * @param reserveFactor The reserve factor **/ function setReserveFactor(DataTypes.ReserveConfigurationMap memory self, uint256 reserveFactor) internal pure { require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.RC_INVALID_RESERVE_FACTOR); self.data = (self.data & RESERVE_FACTOR_MASK) | (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION); } /** * @dev Gets the reserve factor of the reserve * @param self The reserve configuration * @return The reserve factor **/ function getReserveFactor(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; } /** * @dev Gets the configuration flags of the reserve * @param self The reserve configuration * @return The state flags representing active, frozen, borrowing enabled **/ function getFlags(DataTypes.ReserveConfigurationMap storage self) internal view returns ( bool, bool, bool ) { uint256 dataLocal = self.data; return ( (dataLocal & ~ACTIVE_MASK) != 0, (dataLocal & ~FROZEN_MASK) != 0, (dataLocal & ~BORROWING_MASK) != 0 ); } /** * @dev Gets the configuration paramters of the reserve * @param self The reserve configuration * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals **/ function getParams(DataTypes.ReserveConfigurationMap storage self) internal view returns ( uint256, uint256, uint256, uint256, uint256 ) { uint256 dataLocal = self.data; return ( dataLocal & ~LTV_MASK, (dataLocal & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, (dataLocal & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, (dataLocal & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, (dataLocal & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION ); } /** * @dev Gets the configuration paramters of the reserve from a memory object * @param self The reserve configuration * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals **/ function getParamsMemory(DataTypes.ReserveConfigurationMap memory self) internal pure returns ( uint256, uint256, uint256, uint256, uint256 ) { return ( self.data & ~LTV_MASK, (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION ); } /** * @dev Gets the configuration flags of the reserve from a memory object * @param self The reserve configuration * @return The state flags representing active, frozen, borrowing enabled **/ function getFlagsMemory(DataTypes.ReserveConfigurationMap memory self) internal pure returns ( bool, bool, bool ) { return ( (self.data & ~ACTIVE_MASK) != 0, (self.data & ~FROZEN_MASK) != 0, (self.data & ~BORROWING_MASK) != 0 ); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {SafeMath} from './SafeMath.sol'; import {IERC20} from './IERC20.sol'; import {SafeERC20} from './SafeERC20.sol'; import {IXToken} from './IXToken.sol'; import {IVariableDebtToken} from './IVariableDebtToken.sol'; import {IReserveInterestRateStrategy} from './IReserveInterestRateStrategy.sol'; import {ReserveConfiguration} from './ReserveConfiguration.sol'; import {MathUtils} from './MathUtils.sol'; import {WadRayMath} from './WadRayMath.sol'; import {PercentageMath} from './PercentageMath.sol'; import {Errors} from './Errors.sol'; import {DataTypes} from './DataTypes.sol'; /** * @title ReserveLogic library * @author Lever * @notice Implements the logic to update the reserves state */ library ReserveLogic { using SafeMath for uint256; using WadRayMath for uint256; using PercentageMath for uint256; using SafeERC20 for IERC20; /** * @dev Emitted when the state of a reserve is updated * @param asset The address of the underlying asset of the reserve * @param liquidityRate The new liquidity rate * @param variableBorrowRate The new variable borrow rate * @param liquidityIndex The new liquidity index * @param variableBorrowIndex The new variable borrow index **/ event ReserveDataUpdated( address indexed asset, uint256 liquidityRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex ); using ReserveLogic for DataTypes.ReserveData; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; /** * @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 for each unit of asset one unit of income has been accrued * @param reserve The reserve object * @return the normalized income. expressed in ray **/ function getNormalizedIncome(DataTypes.ReserveData storage reserve) internal view returns (uint256) { uint40 timestamp = reserve.lastUpdateTimestamp; //solium-disable-next-line if (timestamp == uint40(block.timestamp)) { //if the index was updated in the same block, no need to perform any calculation return reserve.liquidityIndex; } uint256 cumulated = MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul( reserve.liquidityIndex ); return cumulated; } /** * @dev Returns the ongoing normalized variable debt for the reserve * A value of 1e27 means there is no debt. As time passes, the income is accrued * A value of 2*1e27 means that for each unit of debt, one unit worth of interest has been accumulated * @param reserve The reserve object * @return The normalized variable debt. expressed in ray **/ function getNormalizedDebt(DataTypes.ReserveData storage reserve) internal view returns (uint256) { uint40 timestamp = reserve.lastUpdateTimestamp; //solium-disable-next-line if (timestamp == uint40(block.timestamp)) { //if the index was updated in the same block, no need to perform any calculation return reserve.variableBorrowIndex; } uint256 cumulated = MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul( reserve.variableBorrowIndex ); return cumulated; } /** * @dev Updates the liquidity cumulative index and the variable borrow index. * @param reserve the reserve object **/ function updateState(DataTypes.ReserveData storage reserve) internal { uint256 scaledVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; uint256 previousLiquidityIndex = reserve.liquidityIndex; uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = _updateIndexes( reserve, scaledVariableDebt, previousLiquidityIndex, previousVariableBorrowIndex, lastUpdatedTimestamp ); _mintToTreasury( reserve, scaledVariableDebt, previousVariableBorrowIndex, newLiquidityIndex, newVariableBorrowIndex ); } /** * @dev Accumulates a predefined amount of asset to the reserve as a fixed, instantaneous income. Used for example to accumulate * the flashloan fee to the reserve, and spread it between all the depositors * @param reserve The reserve object * @param totalLiquidity The total liquidity available in the reserve * @param amount The amount to accomulate **/ function cumulateToLiquidityIndex( DataTypes.ReserveData storage reserve, uint256 totalLiquidity, uint256 amount ) internal { uint256 amountToLiquidityRatio = amount.wadToRay().rayDiv(totalLiquidity.wadToRay()); uint256 result = amountToLiquidityRatio.add(WadRayMath.ray()); result = result.rayMul(reserve.liquidityIndex); require(result <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); reserve.liquidityIndex = uint128(result); } /** * @dev Initializes a reserve * @param reserve The reserve object * @param xTokenAddress The address of the overlying xtoken contract * @param interestRateStrategyAddress The address of the interest rate strategy contract **/ function init( DataTypes.ReserveData storage reserve, address xTokenAddress, address variableDebtTokenAddress, address interestRateStrategyAddress ) external { require(reserve.xTokenAddress == address(0), Errors.RL_RESERVE_ALREADY_INITIALIZED); reserve.liquidityIndex = uint128(WadRayMath.ray()); reserve.variableBorrowIndex = uint128(WadRayMath.ray()); reserve.xTokenAddress = xTokenAddress; reserve.variableDebtTokenAddress = variableDebtTokenAddress; reserve.interestRateStrategyAddress = interestRateStrategyAddress; } struct UpdateInterestRatesLocalVars { uint256 availableLiquidity; uint256 newLiquidityRate; uint256 newVariableRate; uint256 totalVariableDebt; } /** * @dev Updates the reserve current variable borrow rate and the current liquidity rate * @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 updateInterestRates( DataTypes.ReserveData storage reserve, address reserveAddress, address xTokenAddress, uint256 liquidityAdded, uint256 liquidityTaken ) internal { UpdateInterestRatesLocalVars memory vars; //calculates the total variable debt locally using the scaled total supply instead //of totalSupply(), as it's noticeably cheaper. Also, the index has been //updated by the previous updateState() call vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress) .scaledTotalSupply() .rayMul(reserve.variableBorrowIndex); vars.availableLiquidity = IERC20(reserveAddress).balanceOf(xTokenAddress); ( vars.newLiquidityRate, vars.newVariableRate ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( vars.availableLiquidity.add(liquidityAdded).sub(liquidityTaken), vars.totalVariableDebt, reserve.configuration.getReserveFactor() ); require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW); require(vars.newVariableRate <= type(uint128).max, Errors.RL_VARIABLE_BORROW_RATE_OVERFLOW); reserve.currentLiquidityRate = uint128(vars.newLiquidityRate); reserve.currentVariableBorrowRate = uint128(vars.newVariableRate); emit ReserveDataUpdated( reserveAddress, vars.newLiquidityRate, vars.newVariableRate, reserve.liquidityIndex, reserve.variableBorrowIndex ); } struct MintToTreasuryLocalVars { uint256 currentVariableDebt; uint256 previousVariableDebt; uint256 totalDebtAccrued; uint256 amountToMint; uint256 reserveFactor; } /** * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the * specific asset. * @param reserve The reserve reserve to be updated * @param scaledVariableDebt The current scaled total variable debt * @param previousVariableBorrowIndex The variable borrow index before the last accumulation of the interest * @param newLiquidityIndex The new liquidity index * @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest **/ function _mintToTreasury( DataTypes.ReserveData storage reserve, uint256 scaledVariableDebt, uint256 previousVariableBorrowIndex, uint256 newLiquidityIndex, uint256 newVariableBorrowIndex ) internal { MintToTreasuryLocalVars memory vars; vars.reserveFactor = reserve.configuration.getReserveFactor(); if (vars.reserveFactor == 0) { return; } //calculate the last principal variable debt vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex); //calculate the new total supply after accumulation of the index vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex); //debt accrued is the sum of the current debt minus the sum of the debt at the last update vars.totalDebtAccrued = vars .currentVariableDebt .sub(vars.previousVariableDebt); vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); if (vars.amountToMint != 0) { IXToken(reserve.xTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex); } } /** * @dev Updates the reserve indexes and the timestamp of the update * @param reserve The reserve reserve to be updated * @param scaledVariableDebt The scaled variable debt * @param liquidityIndex The last stored liquidity index * @param variableBorrowIndex The last stored variable borrow index **/ function _updateIndexes( DataTypes.ReserveData storage reserve, uint256 scaledVariableDebt, uint256 liquidityIndex, uint256 variableBorrowIndex, uint40 timestamp ) internal returns (uint256, uint256) { uint256 currentLiquidityRate = reserve.currentLiquidityRate; uint256 newLiquidityIndex = liquidityIndex; uint256 newVariableBorrowIndex = variableBorrowIndex; //only cumulating if there is any income being produced if (currentLiquidityRate > 0) { uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp); newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex); require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); reserve.liquidityIndex = uint128(newLiquidityIndex); //we need to ensure that there is actual variable debt before accumulating if (scaledVariableDebt != 0) { uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp); newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); require( newVariableBorrowIndex <= type(uint128).max, Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW ); reserve.variableBorrowIndex = uint128(newVariableBorrowIndex); } } //solium-disable-next-line reserve.lastUpdateTimestamp = uint40(block.timestamp); return (newLiquidityIndex, newVariableBorrowIndex); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import {IERC20} from './IERC20.sol'; import {SafeMath} from './SafeMath.sol'; import {Address} from './Address.sol'; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove( IERC20 token, address spender, uint256 value ) internal { require( (value == 0) || (token.allowance(address(this), spender) == 0), 'SafeERC20: approve from non-zero to non-zero allowance' ); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function callOptionalReturn(IERC20 token, bytes memory data) private { require(address(token).isContract(), 'SafeERC20: call to non-contract'); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, 'SafeERC20: low-level call failed'); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed'); } } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, 'SafeMath: addition overflow'); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, 'SafeMath: subtraction overflow'); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, 'SafeMath: multiplication overflow'); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, 'SafeMath: division by zero'); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, 'SafeMath: modulo by zero'); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; library StringLib { function concat(string memory a, string memory b) internal pure returns (string memory) { return string(abi.encodePacked(a, b)); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; 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); } } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {Errors} from './Errors.sol'; import {DataTypes} from './DataTypes.sol'; /** * @title UserConfiguration library * @author Lever * @notice Implements the bitmap logic to handle the user configuration */ library UserConfiguration { uint256 internal constant BORROWING_MASK = 0x5555555555555555555555555555555555555555555555555555555555555555; /** * @dev Sets if the user is borrowing the reserve identified by reserveIndex * @param self The configuration object * @param reserveIndex The index of the reserve in the bitmap * @param borrowing True if the user is borrowing the reserve, false otherwise **/ function setBorrowing( DataTypes.UserConfigurationMap storage self, uint256 reserveIndex, bool borrowing ) internal { require(reserveIndex < 128, Errors.UL_INVALID_INDEX); self.data = (self.data & ~(1 << (reserveIndex * 2))) | (uint256(borrowing ? 1 : 0) << (reserveIndex * 2)); } /** * @dev Sets if the user is using as collateral the reserve identified by reserveIndex * @param self The configuration object * @param reserveIndex The index of the reserve in the bitmap * @param usingAsCollateral True if the user is usin the reserve as collateral, false otherwise **/ function setUsingAsCollateral( DataTypes.UserConfigurationMap storage self, uint256 reserveIndex, bool usingAsCollateral ) internal { require(reserveIndex < 128, Errors.UL_INVALID_INDEX); self.data = (self.data & ~(1 << (reserveIndex * 2 + 1))) | (uint256(usingAsCollateral ? 1 : 0) << (reserveIndex * 2 + 1)); } /** * @dev Used to validate if a user has been using the reserve for borrowing or as collateral * @param self The configuration object * @param reserveIndex The index of the reserve in the bitmap * @return True if the user has been using a reserve for borrowing or as collateral, false otherwise **/ function isUsingAsCollateralOrBorrowing( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { require(reserveIndex < 128, Errors.UL_INVALID_INDEX); return (self.data >> (reserveIndex * 2)) & 3 != 0; } /** * @dev Used to validate if a user has been using the reserve for borrowing * @param self The configuration object * @param reserveIndex The index of the reserve in the bitmap * @return True if the user has been using a reserve for borrowing, false otherwise **/ function isBorrowing(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex) internal pure returns (bool) { require(reserveIndex < 128, Errors.UL_INVALID_INDEX); return (self.data >> (reserveIndex * 2)) & 1 != 0; } /** * @dev Used to validate if a user has been using the reserve as collateral * @param self The configuration object * @param reserveIndex The index of the reserve in the bitmap * @return True if the user has been using a reserve as collateral, false otherwise **/ function isUsingAsCollateral(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex) internal pure returns (bool) { require(reserveIndex < 128, Errors.UL_INVALID_INDEX); return (self.data >> (reserveIndex * 2 + 1)) & 1 != 0; } /** * @dev Used to validate if a user has been borrowing from any reserve * @param self The configuration object * @return True if the user has been borrowing any reserve, false otherwise **/ function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { return self.data & BORROWING_MASK != 0; } /** * @dev Used to validate if a user has not been using any reserve * @param self The configuration object * @return True if the user has been borrowing any reserve, false otherwise **/ function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { return self.data == 0; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {SafeMath} from "./SafeMath.sol"; import {IERC20} from "./IERC20.sol"; import {ReserveLogic} from "./ReserveLogic.sol"; import {GenericLogic} from "./GenericLogic.sol"; import {WadRayMath} from "./WadRayMath.sol"; import {PercentageMath} from "./PercentageMath.sol"; import {SafeERC20} from "./SafeERC20.sol"; import {ReserveConfiguration} from "./ReserveConfiguration.sol"; import {UserConfiguration} from "./UserConfiguration.sol"; import {Errors} from "./Errors.sol"; import {Helpers} from "./Helpers.sol"; import {IReserveInterestRateStrategy} from "./IReserveInterestRateStrategy.sol"; import {DataTypes} from "./DataTypes.sol"; /** * @title ReserveLogic library * @author Lever * @notice Implements functions to validate the different actions of the protocol */ library ValidationLogic { using ReserveLogic for DataTypes.ReserveData; using SafeMath for uint256; using WadRayMath for uint256; using PercentageMath for uint256; using SafeERC20 for IERC20; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using UserConfiguration for DataTypes.UserConfigurationMap; uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 4000; uint256 public constant REBALANCE_UP_USAGE_RATIO_THRESHOLD = 0.95 * 1e27; //usage ratio of 95% /** * @dev Validates a deposit action * @param reserve The reserve object on which the user is depositing * @param amount The amount to be deposited */ function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view { (bool isActive, bool isFrozen,) = reserve.configuration.getFlags(); require(amount != 0, Errors.VL_INVALID_AMOUNT); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isFrozen, Errors.VL_RESERVE_FROZEN); } /** * @dev Validates a withdraw action * @param reserveAddress The address of the reserve * @param amount The amount to be withdrawn * @param userBalance The balance of the user * @param reservesData The reserves state * @param userConfig The user configuration * @param reserves The addresses of the reserves * @param reservesCount The number of reserves * @param oracle The price oracle */ function validateWithdraw( address reserveAddress, uint256 amount, uint256 userBalance, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle ) external view { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); (bool isActive, ,) = reservesData[reserveAddress].configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require( GenericLogic.balanceDecreaseAllowed( reserveAddress, msg.sender, amount, reservesData, userConfig, reserves, reservesCount, oracle ), Errors.VL_TRANSFER_NOT_ALLOWED ); } struct ValidateBorrowLocalVars { uint256 currentLtv; uint256 currentLiquidationThreshold; uint256 amountOfCollateralNeededETH; uint256 userCollateralBalanceETH; uint256 userBorrowBalanceETH; uint256 availableLiquidity; uint256 healthFactor; bool isActive; bool isFrozen; bool borrowingEnabled; } /** * @dev Validates a borrow action * @param reserve The reserve state from which the user is borrowing * @param userAddress The address of the user * @param amount The amount to be borrowed * @param amountInETH The amount to be borrowed, in ETH * @param reservesData The state of all the reserves * @param userConfig The state of the user for the specific reserve * @param reserves The addresses of all the active reserves * @param oracle The price oracle */ function validateBorrow( DataTypes.ReserveData storage reserve, address userAddress, uint256 amount, uint256 amountInETH, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle ) external view { ValidateBorrowLocalVars memory vars; (vars.isActive, vars.isFrozen, vars.borrowingEnabled) = reserve .configuration .getFlags(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!vars.isFrozen, Errors.VL_RESERVE_FROZEN); require(amount != 0, Errors.VL_INVALID_AMOUNT); require(vars.borrowingEnabled, Errors.VL_BORROWING_NOT_ENABLED); ( vars.userCollateralBalanceETH, vars.userBorrowBalanceETH, vars.currentLtv, vars.currentLiquidationThreshold, vars.healthFactor ) = GenericLogic.calculateUserAccountData( userAddress, reservesData, userConfig, reserves, reservesCount, oracle ); require(vars.userCollateralBalanceETH > 0, Errors.VL_COLLATERAL_BALANCE_IS_0); require(vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD); //add the current already borrowed amount to the amount requested to calculate the total collateral needed. vars.amountOfCollateralNeededETH = vars .userBorrowBalanceETH .add(amountInETH) .percentDiv(vars.currentLtv); //LTV is calculated in percentage require( vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH, Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW ); } struct ValidateSwapLocalVars { uint256 currentLtv; uint256 amountOfCollateralNeededETH; uint256 userCollateralBalanceETH; uint256 userBorrowBalanceETH; uint256 healthFactor; } /** * @dev Validates a swap action * @param userAddress The address of the user * @param reservesData The state of all the reserves * @param userConfig The state of the user for the specific reserve * @param reserves The addresses of all the active reserves * @param oracle The price oracle */ function validateSwap( address userAddress, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle ) external view { ValidateSwapLocalVars memory vars; ( vars.userCollateralBalanceETH, vars.userBorrowBalanceETH, vars.currentLtv, , vars.healthFactor ) = GenericLogic.calculateUserAccountData( userAddress, reservesData, userConfig, reserves, reservesCount, oracle ); require(vars.userCollateralBalanceETH > 0, Errors.VL_COLLATERAL_BALANCE_IS_0); require(vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD); //the current already borrowed amount requested to calculate the total collateral needed. vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.percentDiv(vars.currentLtv); //LTV is calculated in percentage require( vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH, Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW ); } /** * @dev Validates a repay action * @param reserve The reserve state from which the user is repaying * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1) * @param onBehalfOf The address of the user msg.sender is repaying for * @param variableDebt The borrow balance of the user */ function validateRepay( DataTypes.ReserveData storage reserve, uint256 amountSent, address onBehalfOf, uint256 variableDebt, uint256 userBalance ) external view { bool isActive = reserve.configuration.getActive(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(amountSent > 0, Errors.VL_INVALID_AMOUNT); require(variableDebt > 0, Errors.VL_NO_DEBT_OF_SELECTED_TYPE); require(userBalance >= amountSent, "deposit is less than debt"); require( amountSent != uint256(-1) || msg.sender == onBehalfOf, Errors.VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF ); } /** * @dev Validates the action of setting an asset as collateral * @param reserve The state of the reserve that the user is enabling or disabling as collateral * @param reserveAddress The address of the reserve * @param reservesData The data of all the reserves * @param userConfig The state of the user for the specific reserve * @param reserves The addresses of all the active reserves * @param oracle The price oracle */ function validateSetUseReserveAsCollateral( DataTypes.ReserveData storage reserve, address reserveAddress, bool useAsCollateral, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle ) external view { uint256 underlyingBalance = IERC20(reserve.xTokenAddress).balanceOf(msg.sender); require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); require( useAsCollateral || GenericLogic.balanceDecreaseAllowed( reserveAddress, msg.sender, underlyingBalance, reservesData, userConfig, reserves, reservesCount, oracle ), Errors.VL_DEPOSIT_ALREADY_IN_USE ); } /** * @dev Validates a flashloan action * @param assets The assets being flashborrowed * @param amounts The amounts for each asset being borrowed **/ function validateFlashloan(address[] memory assets, uint256[] memory amounts) internal pure { require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS); } /** * @dev Validates the liquidation action * @param collateralReserve The reserve data of the collateral * @param principalReserve The reserve data of the principal * @param userConfig The user configuration * @param userHealthFactor The user's health factor * @param userVariableDebt Total variable debt balance of the user **/ function validateLiquidation( DataTypes.ReserveData storage collateralReserve, DataTypes.ReserveData storage principalReserve, DataTypes.UserConfigurationMap storage userConfig, uint256 userHealthFactor, uint256 userVariableDebt ) external view { require( collateralReserve.configuration.getActive() && principalReserve.configuration.getActive(), Errors.VL_NO_ACTIVE_RESERVE ); require( userHealthFactor < GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.MPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD ); bool isCollateralEnabled = collateralReserve.configuration.getLiquidationThreshold() > 0 && userConfig.isUsingAsCollateral(collateralReserve.id); //if collateral isn't enabled as collateral by user, it cannot be liquidated require( isCollateralEnabled, Errors.MPCM_COLLATERAL_CANNOT_BE_LIQUIDATED ); require( userVariableDebt > 0, Errors.MPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER ); } /** * @dev Validates an xToken transfer * @param from The user from which the xTokens are being transferred * @param reservesData The state of all the reserves * @param userConfig The state of the user for the specific reserve * @param reserves The addresses of all the active reserves * @param oracle The price oracle */ function validateTransfer( address from, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle ) internal view { (, , , , uint256 healthFactor) = GenericLogic.calculateUserAccountData( from, reservesData, userConfig, reserves, reservesCount, oracle ); require( healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.VL_TRANSFER_NOT_ALLOWED ); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {VariableDebtToken} from './VariableDebtToken.sol'; import {Ownable} from './Ownable.sol'; import {StringLib} from './StringLib.sol'; contract VariableTokensHelper is Ownable { address payable private pool; address private addressesProvider; event deployedContracts(address variableToken); constructor(address payable _pool, address _addressesProvider) public { pool = _pool; addressesProvider = _addressesProvider; } function initDeployment( address[] calldata tokens, string[] calldata symbols, uint8[] calldata decimals ) external onlyOwner { require(tokens.length == symbols.length, 'Arrays not same length'); require(pool != address(0), 'Pool can not be zero address'); for (uint256 i = 0; i < tokens.length; i++) { emit deployedContracts( address( new VariableDebtToken( addressesProvider, tokens[i], StringLib.concat('Lever variable debt bearing ', symbols[i]), StringLib.concat('d', symbols[i]), decimals[i] ) ) ); } } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @title VersionedInitializable * * @dev Helper contract to implement initializer functions. To use it, replace * the constructor with a function that has the `initializer` modifier. * WARNING: Unlike constructors, initializer functions must be manually * invoked. This applies both to deploying an Initializable contract, as well * as extending an Initializable contract via inheritance. * WARNING: When used with inheritance, manual care must be taken to not invoke * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. * * @author Lever, inspired by the OpenZeppelin Initializable contract */ abstract 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 virtual 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; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {Errors} from './Errors.sol'; /** * @title WadRayMath library * @author Lever * @dev Provides mul and div function for wads (decimal numbers with 18 digits precision) and rays (decimals with 27 digits) **/ library WadRayMath { uint256 internal constant WAD = 1e18; uint256 internal constant halfWAD = WAD / 2; uint256 internal constant RAY = 1e27; uint256 internal constant halfRAY = RAY / 2; uint256 internal constant WAD_RAY_RATIO = 1e9; /** * @return One ray, 1e27 **/ function ray() internal pure returns (uint256) { return RAY; } /** * @return One wad, 1e18 **/ function wad() internal pure returns (uint256) { return WAD; } /** * @return Half ray, 1e27/2 **/ function halfRay() internal pure returns (uint256) { return halfRAY; } /** * @return Half ray, 1e18/2 **/ function halfWad() internal pure returns (uint256) { return halfWAD; } /** * @dev Multiplies two wad, rounding half up to the nearest wad * @param a Wad * @param b Wad * @return The result of a*b, in wad **/ function wadMul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0 || b == 0) { return 0; } require(a <= (type(uint256).max - halfWAD) / b, Errors.MATH_MULTIPLICATION_OVERFLOW); return (a * b + halfWAD) / WAD; } /** * @dev Divides two wad, rounding half up to the nearest wad * @param a Wad * @param b Wad * @return The result of a/b, in wad **/ function wadDiv(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, Errors.MATH_DIVISION_BY_ZERO); uint256 halfB = b / 2; require(a <= (type(uint256).max - halfB) / WAD, Errors.MATH_MULTIPLICATION_OVERFLOW); return (a * WAD + halfB) / b; } /** * @dev Multiplies two ray, rounding half up to the nearest ray * @param a Ray * @param b Ray * @return The result of a*b, in ray **/ function rayMul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0 || b == 0) { return 0; } require(a <= (type(uint256).max - halfRAY) / b, Errors.MATH_MULTIPLICATION_OVERFLOW); return (a * b + halfRAY) / RAY; } /** * @dev Divides two ray, rounding half up to the nearest ray * @param a Ray * @param b Ray * @return The result of a/b, in ray **/ function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, Errors.MATH_DIVISION_BY_ZERO); uint256 halfB = b / 2; require(a <= (type(uint256).max - halfB) / RAY, Errors.MATH_MULTIPLICATION_OVERFLOW); return (a * RAY + halfB) / b; } /** * @dev Casts ray down to wad * @param a Ray * @return a casted to wad, rounded half up to the nearest wad **/ function rayToWad(uint256 a) internal pure returns (uint256) { uint256 halfRatio = WAD_RAY_RATIO / 2; uint256 result = halfRatio + a; require(result >= halfRatio, Errors.MATH_ADDITION_OVERFLOW); return result / WAD_RAY_RATIO; } /** * @dev Converts wad up to ray * @param a Wad * @return a converted in ray **/ function wadToRay(uint256 a) internal pure returns (uint256) { uint256 result = a * WAD_RAY_RATIO; require(result / WAD_RAY_RATIO == a, Errors.MATH_MULTIPLICATION_OVERFLOW); return result; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {Ownable} from './Ownable.sol'; import {IERC20} from './IERC20.sol'; import {IWETH} from './IWETH.sol'; import {IWETHGateway} from './IWETHGateway.sol'; import {IMarginPool} from './IMarginPool.sol'; import {IXToken} from './IXToken.sol'; import {ICreditDelegationToken} from './ICreditDelegationToken.sol'; import {ReserveConfiguration} from './ReserveConfiguration.sol'; import {UserConfiguration} from './UserConfiguration.sol'; import {Helpers} from './Helpers.sol'; import {DataTypes} from './DataTypes.sol'; contract WETHGateway is IWETHGateway, Ownable { using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using UserConfiguration for DataTypes.UserConfigurationMap; IWETH internal immutable WETH; IMarginPool internal immutable POOL; IXToken internal immutable xWETH; ICreditDelegationToken internal immutable dWETH; /** * @dev Sets the WETH address and the MarginPoolAddressesProvider address. Infinite approves margin pool. * @param weth Address of the Wrapped Ether contract * @param pool Address of the MarginPool contract **/ constructor(address weth, address pool) public { IMarginPool poolInstance = IMarginPool(pool); WETH = IWETH(weth); POOL = poolInstance; xWETH = IXToken(poolInstance.getReserveData(weth).xTokenAddress); dWETH = ICreditDelegationToken(poolInstance.getReserveData(weth).variableDebtTokenAddress); IWETH(weth).approve(pool, uint256(-1)); } /** * @dev deposits WETH into the reserve, using native ETH. A corresponding amount of the overlying asset (xTokens) * is minted. * @param onBehalfOf address of the user who will receive the xTokens representing the deposit **/ function depositETH(address onBehalfOf) external payable override { WETH.deposit{value: msg.value}(); POOL.deposit(address(WETH), msg.value, onBehalfOf); } /** * @dev withdraws the WETH _reserves of msg.sender. * @param amount amount of xWETH to withdraw and receive native ETH * @param to address of the user who will receive native ETH */ function withdrawETH(uint256 amount, address to) external override { uint256 userBalance = xWETH.balanceOf(msg.sender); uint256 amountToWithdraw = amount; // if amount is equal to uint(-1), the user wants to redeem everything if (amount == type(uint256).max) { amountToWithdraw = userBalance; } xWETH.transferFrom(msg.sender, address(this), amountToWithdraw); POOL.withdraw(address(WETH), amountToWithdraw, address(this)); WETH.withdraw(amountToWithdraw); _safeTransferETH(to, amountToWithdraw); } /** * @dev borrow WETH, unwraps to ETH and send both the ETH and DebtTokens to msg.sender, via `approveDelegation` and onBehalf argument in `MarginPool.borrow`. * @param amount the amount of ETH to borrow */ function borrowETH( uint256 amount ) external override { POOL.borrow(address(WETH), amount, msg.sender); WETH.withdraw(amount); _safeTransferETH(msg.sender, amount); } /** * @dev transfer ETH to an address, revert if it fails. * @param to recipient of the transfer * @param value the amount to send */ function _safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success, 'ETH_TRANSFER_FAILED'); } /** * @dev transfer ERC20 from the utility contract, for ERC20 recovery in case of stuck tokens due * direct transfers to the contract address. * @param token token to transfer * @param to recipient of the transfer * @param amount amount to send */ function emergencyTokenTransfer( address token, address to, uint256 amount ) external onlyOwner { IERC20(token).transfer(to, amount); } /** * @dev transfer native Ether from the utility contract, for native Ether recovery in case of stuck Ether * due selfdestructs or transfer ether to pre-computated contract address before deployment. * @param to recipient of the transfer * @param amount amount to send */ function emergencyEtherTransfer(address to, uint256 amount) external onlyOwner { _safeTransferETH(to, amount); } /** * @dev Get WETH address used by WETHGateway */ function getWETHAddress() external view returns (address) { return address(WETH); } /** * @dev Get xWETH address used by WETHGateway */ function getXWETHAddress() external view returns (address) { return address(xWETH); } /** * @dev Get MarginPool address used by WETHGateway */ function getMarginPoolAddress() external view returns (address) { return address(POOL); } /** * @dev Only WETH contract is allowed to transfer ETH here. Prevent other addresses to send Ether to this contract. */ receive() external payable { require(msg.sender == address(WETH), 'Receive not allowed'); } /** * @dev Revert fallback calls */ fallback() external payable { revert('Fallback not allowed'); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IERC20} from "./IERC20.sol"; import {SafeERC20} from "./SafeERC20.sol"; import {IMarginPool} from "./IMarginPool.sol"; import {IXToken} from "./IXToken.sol"; import {WadRayMath} from "./WadRayMath.sol"; import {Errors} from "./Errors.sol"; import {IncentivizedERC20} from "./IncentivizedERC20.sol"; import {SafeMath} from "./SafeMath.sol"; import { IMarginPoolAddressesProvider } from "./IMarginPoolAddressesProvider.sol"; import {Address} from "./Address.sol"; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2); } } /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier * available, which can be aplied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. */ contract ReentrancyGuard { /// @dev counter to allow mutex lock with only one SSTORE operation uint256 private _guardCounter; constructor() internal { // The counter starts at one to prevent changing it from zero to a non-zero // value, which is a more expensive operation. _guardCounter = 1; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { _guardCounter += 1; uint256 localCounter = _guardCounter; _; require( localCounter == _guardCounter, "ReentrancyGuard: reentrant call" ); } } /** * @title Lever ERC20 XToken * @dev Implementation of the interest bearing token for the Lever protocol * @author Lever */ contract XToken is IncentivizedERC20, IXToken, ReentrancyGuard { using WadRayMath for uint256; using SafeERC20 for IERC20; // address public rewardsDistribution; IERC20 public rewardsToken; uint256 public periodFinish = 0; uint256 public rewardRate = 0; uint256 public rewardsDuration = 30 days; uint256 public lastUpdateTime; uint256 public rewardPerTokenStored; mapping(address => uint256) public userRewardPerTokenPaid; mapping(address => uint256) public rewards; bytes public constant EIP712_REVISION = bytes("1"); bytes32 internal constant EIP712_DOMAIN = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); bytes32 public constant PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); uint256 public constant UINT_MAX_VALUE = uint256(-1); address public immutable UNDERLYING_ASSET_ADDRESS; address public immutable RESERVE_TREASURY_ADDRESS; IMarginPool public immutable POOL; IMarginPoolAddressesProvider public addressesProvider; /// @dev owner => next valid nonce to submit with permit() mapping(address => uint256) public _nonces; bytes32 public DOMAIN_SEPARATOR; /* ========== MODIFIERS ========== */ modifier updateReward(address account) { rewardPerTokenStored = rewardPerToken(); lastUpdateTime = lastTimeRewardApplicable(); if (account != address(0)) { rewards[account] = earned(account); userRewardPerTokenPaid[account] = rewardPerTokenStored; } _; } modifier onlyRewardsDistribution() { require( msg.sender == addressesProvider.getRewardsDistribution(), "Caller is not RewardsDistribution contract" ); _; } modifier onlyMarginPool { require( _msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_MARGIN_POOL ); _; } constructor( address _addressesProvider, address underlyingAssetAddress, string memory tokenName, string memory tokenSymbol, uint8 decimals ) public IncentivizedERC20(tokenName, tokenSymbol, decimals) { addressesProvider = IMarginPoolAddressesProvider(_addressesProvider); POOL = IMarginPool(addressesProvider.getMarginPool()); UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; RESERVE_TREASURY_ADDRESS = addressesProvider.getTreasuryAddress(); // rewardsDistribution = addressesProvider.getRewardsDistribution(); rewardsToken = IERC20(IMarginPoolAddressesProvider(_addressesProvider).getLeverToken()); } /** * @dev Burns xTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying` * - Only callable by the MarginPool, as extra state updates there need to be managed * @param user The owner of the xTokens, getting them burned * @param receiverOfUnderlying The address that will receive the underlying * @param amount The amount being burned * @param index The new liquidity index of the reserve **/ function burn( address user, address receiverOfUnderlying, uint256 amount, uint256 index ) external override onlyMarginPool updateReward(user) { uint256 amountScaled = amount.rayDiv(index); require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT); _burn(user, amountScaled); if (receiverOfUnderlying != address(this)) { IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer( receiverOfUnderlying, amount ); } emit Transfer(user, address(0), amount); emit Burn(user, receiverOfUnderlying, amount, index); } /** * @dev Mints `amount` xTokens to `user` * - Only callable by the MarginPool, as extra state updates there need to be managed * @param user The address receiving the minted tokens * @param amount The amount of tokens getting minted * @param index The new liquidity index of the reserve * @return `true` if the the previous balance of the user was 0 */ function mint( address user, uint256 amount, uint256 index ) external override onlyMarginPool updateReward(user) returns (bool) { uint256 previousBalance = super.balanceOf(user); uint256 amountScaled = amount.rayDiv(index); require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT); _mint(user, amountScaled); emit Transfer(address(0), user, amount); emit Mint(user, amount, index); return previousBalance == 0; } /** * @dev Mints xTokens to the reserve treasury * - Only callable by the MarginPool * @param amount The amount of tokens getting minted * @param index The new liquidity index of the reserve */ function mintToTreasury(uint256 amount, uint256 index) external override onlyMarginPool updateReward(RESERVE_TREASURY_ADDRESS) { if (amount == 0) { return; } // Compared to the normal mint, we don't check for rounding errors. // The amount to mint can easily be very small since it is a fraction of the interest ccrued. // In that case, the treasury will experience a (very small) loss, but it // wont cause potentially valid transactions to fail. _mint(RESERVE_TREASURY_ADDRESS, amount.rayDiv(index)); emit Transfer(address(0), RESERVE_TREASURY_ADDRESS, amount); emit Mint(RESERVE_TREASURY_ADDRESS, amount, index); } /** * @dev Transfers xTokens in the event of a borrow being liquidated, in case the liquidators reclaims the xToken * - Only callable by the MarginPool * @param from The address getting liquidated, current owner of the xTokens * @param to The recipient * @param value The amount of tokens getting transferred **/ function transferOnLiquidation( address from, address to, uint256 value ) external override onlyMarginPool updateReward(from) updateReward(to) { // Being a normal transfer, the Transfer() and BalanceTransfer() are emitted // so no need to emit a specific event here _transfer(from, to, value, false); emit Transfer(from, to, value); } /** * @dev Calculates the balance of the user: principal balance + interest generated by the principal * @param user The user whose balance is calculated * @return The balance of the user **/ function balanceOf(address user) public view override(IncentivizedERC20, IERC20) returns (uint256) { return super.balanceOf(user).rayMul( POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS) ); } /** * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the * updated stored balance divided by the reserve's liquidity index at the moment of the update * @param user The user whose balance is calculated * @return The scaled balance of the user **/ function scaledBalanceOf(address user) external view override returns (uint256) { return super.balanceOf(user); } /** * @dev Returns the scaled balance of the user and the scaled total supply. * @param user The address of the user * @return The scaled balance of the user * @return The scaled balance and the scaled total supply **/ function getScaledUserBalanceAndSupply(address user) external view override returns (uint256, uint256) { return (super.balanceOf(user), super.totalSupply()); } /** * @dev calculates the total supply of the specific xToken * 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 override(IncentivizedERC20, IERC20) returns (uint256) { uint256 currentSupplyScaled = super.totalSupply(); if (currentSupplyScaled == 0) { return 0; } return currentSupplyScaled.rayMul( POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS) ); } /** * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) * @return the scaled total supply **/ function scaledTotalSupply() public view virtual override returns (uint256) { return super.totalSupply(); } /** * @dev Transfers the underlying asset to `target`. Used by the MarginPool to transfer * assets in borrow(), withdraw() * @param target The recipient of the xTokens * @param amount The amount getting transferred * @return The amount transferred **/ function transferUnderlyingTo(address target, uint256 amount) external override onlyMarginPool returns (uint256) { IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(target, amount); return amount; } /** * @dev implements the permit function as for * https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md * @param owner The owner of the funds * @param spender The spender * @param value The amount * @param deadline The deadline timestamp, type(uint256).max for max deadline * @param v Signature param * @param s Signature param * @param r Signature param */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require(owner != address(0), "INVALID_OWNER"); //solium-disable-next-line require(block.timestamp <= deadline, "INVALID_EXPIRATION"); uint256 currentValidNonce = _nonces[owner]; bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256( abi.encode( PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline ) ) ) ); require(owner == ecrecover(digest, v, r, s), "INVALID_SIGNATURE"); _nonces[owner] = currentValidNonce.add(1); _approve(owner, spender, value); } /** * @dev Transfers the xTokens between two users. Validates the transfer * (ie checks for valid HF after the transfer) if required * @param from The source address * @param to The destination address * @param amount The amount getting transferred * @param validate `true` if the transfer needs to be validated **/ function _transfer( address from, address to, uint256 amount, bool validate ) internal updateReward(from) updateReward(to) { uint256 index = POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS); uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index); uint256 toBalanceBefore = super.balanceOf(to).rayMul(index); super._transfer(from, to, amount.rayDiv(index)); if (validate) { POOL.finalizeTransfer( UNDERLYING_ASSET_ADDRESS, from, to, amount, fromBalanceBefore, toBalanceBefore ); } emit BalanceTransfer(from, to, amount, index); } /** * @dev Overrides the parent _transfer to force validated transfer() and transferFrom() * @param from The source address * @param to The destination address * @param amount The amount getting transferred **/ function _transfer( address from, address to, uint256 amount ) internal override { _transfer(from, to, amount, true); } function lastTimeRewardApplicable() public view returns (uint256) { return Math.min(block.timestamp, periodFinish); } function rewardPerToken() public view returns (uint256) { if (totalSupply() == 0) { return rewardPerTokenStored; } return rewardPerTokenStored.add( lastTimeRewardApplicable() .sub(lastUpdateTime) .mul(rewardRate) .mul(1e18) .div(totalSupply()) ); } function earned(address account) public view returns (uint256) { return balanceOf(account) .mul(rewardPerToken().sub(userRewardPerTokenPaid[account])) .div(1e18) .add(rewards[account]); } function getRewardForDuration() external view returns (uint256) { return rewardRate.mul(rewardsDuration); } function getReward() public nonReentrant updateReward(msg.sender) { uint256 reward = rewards[msg.sender]; require(reward > 0); rewards[msg.sender] = 0; rewardsToken.safeTransfer(msg.sender, reward); emit RewardPaid(msg.sender, reward); } /* ========== RESTRICTED FUNCTIONS ========== */ function notifyRewardAmount(uint256 reward, uint256 _rewardsDuration) external onlyRewardsDistribution updateReward(address(0)) { // Ensure the provided reward amount is not more than the balance in the contract. // This keeps the reward rate in the right range, preventing overflows due to // very high values of rewardRate in the earned and rewardsPerToken functions; // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. uint256 balance = rewardsToken.balanceOf(address(this)); if (block.timestamp >= periodFinish) { rewardsDuration = _rewardsDuration; rewardRate = reward.div(rewardsDuration); require( rewardRate <= balance.div(rewardsDuration), "Provided reward too high" ); periodFinish = block.timestamp.add(rewardsDuration); } else { uint256 remaining = periodFinish.sub(block.timestamp); uint256 leftover = remaining.mul(rewardRate); rewardRate = reward.add(leftover).div(remaining); require( rewardRate <= balance.div(remaining), "Provided reward too high" ); } lastUpdateTime = block.timestamp; emit RewardAdded(reward, _rewardsDuration); } /* ========== EVENTS ========== */ event RewardAdded(uint256 reward, uint256 _rewardsDuration); event RewardPaid(address indexed user, uint256 reward); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {IMarginPoolAddressesProvider} from './IMarginPoolAddressesProvider.sol'; import {MarginPoolConfigurator} from './MarginPoolConfigurator.sol'; import {XToken} from './XToken.sol'; import { DefaultReserveInterestRateStrategy } from './DefaultReserveInterestRateStrategy.sol'; import {Ownable} from './Ownable.sol'; import {StringLib} from './StringLib.sol'; contract XTokensAndRatesHelper is Ownable { address payable private pool; address private addressesProvider; address private poolConfigurator; event deployedContracts(address xToken, address strategy); constructor( address _addressesProvider, address _poolConfigurator ) public { addressesProvider = _addressesProvider; poolConfigurator = _poolConfigurator; } function initDeployment( address[] calldata assets, string[] calldata symbols, uint256[4][] calldata rates, uint8[] calldata decimals ) external onlyOwner { require(assets.length == symbols.length, 't Arrays not same length'); require(rates.length == symbols.length, 'r Arrays not same length'); for (uint256 i = 0; i < assets.length; i++) { emit deployedContracts( address( new XToken( addressesProvider, assets[i], StringLib.concat('Lever interest bearing ', symbols[i]), StringLib.concat('x', symbols[i]), decimals[i] ) ), address( new DefaultReserveInterestRateStrategy( IMarginPoolAddressesProvider(addressesProvider), rates[i][0], rates[i][1], rates[i][2], rates[i][3] ) ) ); } } function initReserve( address[] calldata variables, address[] calldata xTokens, address[] calldata strategies, uint8[] calldata reserveDecimals ) external onlyOwner { require(xTokens.length == variables.length); require(strategies.length == variables.length); require(reserveDecimals.length == variables.length); for (uint256 i = 0; i < variables.length; i++) { MarginPoolConfigurator(poolConfigurator).initReserve( xTokens[i], variables[i], reserveDecimals[i], strategies[i] ); } } function configureReserves( address[] calldata assets, uint256[] calldata baseLTVs, uint256[] calldata liquidationThresholds, uint256[] calldata liquidationBonuses, uint256[] calldata reserveFactors ) external onlyOwner { require(baseLTVs.length == assets.length); require(liquidationThresholds.length == assets.length); require(liquidationBonuses.length == assets.length); require(reserveFactors.length == assets.length); MarginPoolConfigurator configurator = MarginPoolConfigurator(poolConfigurator); for (uint256 i = 0; i < assets.length; i++) { configurator.configureReserveAsCollateral( assets[i], baseLTVs[i], liquidationThresholds[i], liquidationBonuses[i] ); configurator.setReserveFactor(assets[i], reserveFactors[i]); } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_addressesProvider","type":"address"},{"internalType":"address","name":"underlyingAsset","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fromUser","type":"address"},{"indexed":true,"internalType":"address","name":"toUser","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BorrowAllowanceDelegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DEBT_TOKEN_REVISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL","outputs":[{"internalType":"contract IMarginPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNDERLYING_ASSET_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"addressesProvider","outputs":[{"internalType":"contract IMarginPoolAddressesProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveDelegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromUser","type":"address"},{"internalType":"address","name":"toUser","type":"address"}],"name":"borrowAllowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getScaledUserBalanceAndSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"periodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"scaledBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scaledTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRewardPerTokenPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60c060405260006009556000600a5562278d00600b553480156200002257600080fd5b506040516200231738038062002317833981810160405260a08110156200004857600080fd5b815160208301516040808501805191519395929483019291846401000000008211156200007457600080fd5b9083019060208201858111156200008a57600080fd5b8251640100000000811182820188101715620000a557600080fd5b82525081516020918201929091019080838360005b83811015620000d4578181015183820152602001620000ba565b50505050905090810190601f168015620001025780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200012657600080fd5b9083019060208201858111156200013c57600080fd5b82516401000000008111828201881017156200015757600080fd5b82525081516020918201929091019080838360005b83811015620001865781810151838201526020016200016c565b50505050905090810190601f168015620001b45780820380516001836020036101000a031916815260200191505b506040818152602092830151633aa431a160e11b835290519094506001600160a01b0389169350637548634292600480840193919291829003018186803b158015620001ff57600080fd5b505afa15801562000214573d6000803e3d6000fd5b505050506040513d60208110156200022b57600080fd5b505183518590859085908590839083908390620002509060039060208601906200034d565b508151620002669060049060208501906200034d565b506005805460ff191660ff929092169190911790555050505050606091821b6001600160601b031990811660a052911b166080526001600755601080546001600160a01b0319166001600160a01b038716908117909155604080516376083b5b60e01b815290516376083b5b91600480820192602092909190829003018186803b158015620002f457600080fd5b505afa15801562000309573d6000803e3d6000fd5b505050506040513d60208110156200032057600080fd5b5051600880546001600160a01b0319166001600160a01b0390921691909117905550620003e99350505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200039057805160ff1916838001178555620003c0565b82800160010185558215620003c0579182015b82811115620003c0578251825591602001919060010190620003a3565b50620003ce929150620003d2565b5090565b5b80821115620003ce5760008155600101620003d3565b60805160601c60a05160601c611edc6200043b600039806107dc5280610dfe5280610ec55280610fa0528061136952508061080b5280610e2d5280610f7052806112a052806118705250611edc6000f3fe608060405234801561001057600080fd5b506004361061021b5760003560e01c806380faa57d11610125578063c04a8a10116100ad578063d1af0c7d1161007c578063d1af0c7d146105df578063dd62ed3e146105e7578063df136d6514610615578063ebe2b12b1461061d578063f5298aca146106255761021b565b8063c04a8a101461059b578063c72c4d10146105c7578063c8f33c91146105cf578063cd3daf9d146105d75761021b565b8063a9059cbb116100f4578063a9059cbb1461051b578063b16a19de14610547578063b1bf962d1461054f578063b3f1c93d14610557578063b9a7b622146105935761021b565b806380faa57d146104e55780638b876347146104ed57806395d89b4114610513578063a457c2d7146104315761021b565b8063246132f9116101a85780633d18b912116101775780633d18b9121461045d5780636bd76d241461046557806370a08231146104935780637535d246146104b95780637b0a47ee146104dd5761021b565b8063246132f9146103e6578063313ce5671461040b578063386a95251461042957806339509351146104315761021b565b80630afbcdc9116101ef5780630afbcdc91461033b57806318160ddd1461037a5780631c1f78eb146103825780631da24f3e1461038a57806323b872dd146103b05761021b565b80628cc2621461022057806306fdde03146102585780630700037d146102d5578063095ea7b3146102fb575b600080fd5b6102466004803603602081101561023657600080fd5b50356001600160a01b0316610657565b60408051918252519081900360200190f35b6102606106c7565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561029a578181015183820152602001610282565b50505050905090810190601f1680156102c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610246600480360360208110156102eb57600080fd5b50356001600160a01b031661075e565b6103276004803603604081101561031157600080fd5b506001600160a01b038135169060200135610770565b604080519115158252519081900360200190f35b6103616004803603602081101561035157600080fd5b50356001600160a01b03166107b8565b6040805192835260208301919091528051918290030190f35b6102466107d5565b6102466108a6565b610246600480360360208110156103a057600080fd5b50356001600160a01b03166108bf565b610327600480360360608110156103c657600080fd5b506001600160a01b038135811691602081013590911690604001356108ca565b610409600480360360408110156103fc57600080fd5b5080359060200135610912565b005b610413610c17565b6040805160ff9092168252519081900360200190f35b610246610c20565b6103276004803603604081101561044757600080fd5b506001600160a01b038135169060200135610c26565b610409610c75565b6102466004803603604081101561047b57600080fd5b506001600160a01b0381358116916020013516610daf565b610246600480360360208110156104a957600080fd5b50356001600160a01b0316610ddc565b6104c1610ec3565b604080516001600160a01b039092168252519081900360200190f35b610246610ee7565b610246610eed565b6102466004803603602081101561050357600080fd5b50356001600160a01b0316610efb565b610260610f0d565b6103276004803603604081101561053157600080fd5b506001600160a01b0381351690602001356108ca565b6104c1610f6e565b610246610f92565b6103276004803603608081101561056d57600080fd5b506001600160a01b03813581169160208101359091169060408101359060600135610f9c565b61024661122a565b610409600480360360408110156105b157600080fd5b506001600160a01b03813516906020013561122f565b6104c16112e9565b6102466112f8565b6102466112fe565b6104c161134c565b610246600480360360408110156105fd57600080fd5b506001600160a01b0381358116916020013516610c26565b61024661135b565b610246611361565b6104096004803603606081101561063b57600080fd5b506001600160a01b038135169060208101359060400135611367565b6001600160a01b0381166000908152600f6020908152604080832054600e9092528220546106bf91906106b990670de0b6b3a7640000906106b3906106a49061069e6112fe565b90611570565b6106ad88610ddc565b906115b2565b9061160b565b9061164d565b90505b919050565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156107535780601f1061072857610100808354040283529160200191610753565b820191906000526020600020905b81548152906001019060200180831161073657829003601f168201915b505050505090505b90565b600f6020526000908152604090205481565b6040805162461bcd60e51b81526020600482015260166024820152751054141493d5905317d393d517d4d5541413d495115160521b6044820152905160009181900360640190fd5b6000806107c4836116a7565b6107cc6116c2565b91509150915091565b60006108a17f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663386497fd7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561086757600080fd5b505afa15801561087b573d6000803e3d6000fd5b505050506040513d602081101561089157600080fd5b505161089b6116c2565b906116c8565b905090565b60006108a1600b54600a546115b290919063ffffffff16565b60006106bf826116a7565b6040805162461bcd60e51b81526020600482015260166024820152751514905394d1915497d393d517d4d5541413d495115160521b6044820152905160009181900360640190fd5b601060009054906101000a90046001600160a01b03166001600160a01b031663f2fbb22d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561096057600080fd5b505afa158015610974573d6000803e3d6000fd5b505050506040513d602081101561098a57600080fd5b50516001600160a01b031633146109d25760405162461bcd60e51b815260040180806020018281038252602a815260200180611e53602a913960400191505060405180910390fd5b60006109dc6112fe565b600d556109e7610eed565b600c556001600160a01b03811615610a2e57610a0281610657565b6001600160a01b0382166000908152600f6020908152604080832093909355600d54600e909152919020555b600854604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015610a7957600080fd5b505afa158015610a8d573d6000803e3d6000fd5b505050506040513d6020811015610aa357600080fd5b50516009549091504210610b3857600b839055610ac0848461160b565b600a55600b54610ad190829061160b565b600a541115610b22576040805162461bcd60e51b81526020600482015260186024820152770a0e4deecd2c8cac840e4caeec2e4c840e8dede40d0d2ced60431b604482015290519081900360640190fd5b600b54610b3090429061164d565b600955610bd2565b600954600090610b489042611570565b90506000610b61600a54836115b290919063ffffffff16565b9050610b71826106b3888461164d565b600a55610b7e838361160b565b600a541115610bcf576040805162461bcd60e51b81526020600482015260186024820152770a0e4deecd2c8cac840e4caeec2e4c840e8dede40d0d2ced60431b604482015290519081900360640190fd5b50505b42600c55604080518581526020810185905281517f6c07ee05dcf262f13abf9d87b846ee789d2f90fe991d495acd7d7fc109ee1f55929181900390910190a150505050565b60055460ff1690565b600b5481565b6040805162461bcd60e51b815260206004820152601760248201527f414c4c4f57414e43455f4e4f545f535550504f525445440000000000000000006044820152905160009181900360640190fd5b600780546001019081905533610c896112fe565b600d55610c94610eed565b600c556001600160a01b03811615610cdb57610caf81610657565b6001600160a01b0382166000908152600f6020908152604080832093909355600d54600e909152919020555b336000908152600f602052604090205480610cf557600080fd5b336000818152600f6020526040812055600854610d1e916001600160a01b039091169083611786565b60408051828152905133917fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486919081900360200190a250506007548114610dac576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b50565b6001600160a01b038083166000908152600660209081526040808320938516835292905220545b92915050565b600080610de8836116a7565b905080610df95760009150506106c2565b610ebc7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663386497fd7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610e8957600080fd5b505afa158015610e9d573d6000803e3d6000fd5b505050506040513d6020811015610eb357600080fd5b505182906116c8565b9392505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600a5481565b60006108a1426009546117dd565b600e6020526000908152604090205481565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156107535780601f1061072857610100808354040283529160200191610753565b7f000000000000000000000000000000000000000000000000000000000000000081565b60006108a16116c2565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610fd06117f3565b6001600160a01b03161460405180604001604052806002815260200161323960f01b8152509061107e5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561104357818101518382015260200161102b565b50505050905090810190601f1680156110705780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50836110886112fe565b600d55611093610eed565b600c556001600160a01b038116156110da576110ae81610657565b6001600160a01b0382166000908152600f6020908152604080832093909355600d54600e909152919020555b846001600160a01b0316866001600160a01b0316146110fe576110fe8587866117f7565b6000611109866116a7565b9050600061111786866118d4565b6040805180820190915260028152611a9b60f11b60208201529091508161117f5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b5061118a87826119db565b6040805187815290516001600160a01b038916916000917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a3866001600160a01b0316886001600160a01b03167f2f00e3cdd69a77be7ed215ec7b2a36784dd158f921fca79ac29deffa353fe6ee8888604051808381526020018281526020019250505060405180910390a350159695505050505050565b600181565b806006600061123c6117f3565b6001600160a01b03908116825260208083019390935260409182016000908120918716808252919093529120919091556112746117f3565b6001600160a01b03167fda919360433220e13b51e8c211e490d148e61a3bd53de8c097194e458b97f3e17f00000000000000000000000000000000000000000000000000000000000000008460405180836001600160a01b031681526020018281526020019250505060405180910390a35050565b6010546001600160a01b031681565b600c5481565b60006113086107d5565b6113155750600d5461075b565b6108a16113436113236107d5565b6106b3670de0b6b3a76400006106ad600a546106ad600c5461069e610eed565b600d549061164d565b6008546001600160a01b031681565b600d5481565b60095481565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166113996117f3565b6001600160a01b03161460405180604001604052806002815260200161323960f01b8152509061140a5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b50826114146112fe565b600d5561141f610eed565b600c556001600160a01b038116156114665761143a81610657565b6001600160a01b0382166000908152600f6020908152604080832093909355600d54600e909152919020555b600061147284846118d4565b60408051808201909152600281526106a760f31b6020820152909150816114da5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b506114e58582611a97565b6040805185815290516000916001600160a01b038816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a3604080518581526020810185905281516001600160a01b038816927f49995e5dd6158cf69ad3e9777c46755a1a826a446c6416992167462dad033b2a928290030190a25050505050565b6000610ebc83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611b35565b6000826115c157506000610dd6565b828202828482816115ce57fe5b0414610ebc5760405162461bcd60e51b8152600401808060200182810382526021815260200180611e116021913960400191505060405180910390fd5b6000610ebc83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250611b8f565b600082820183811015610ebc576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6001600160a01b031660009081526020819052604090205490565b60025490565b60008215806116d5575081155b156116e257506000610dd6565b816b019d971e4fe8401e7400000019816116f857fe5b0483111560405180604001604052806002815260200161068760f31b815250906117635760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b50506b033b2e3c9fd0803ce800000091026b019d971e4fe8401e74000000010490565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526117d8908490611bf4565b505050565b60008183106117ec5781610ebc565b5090919050565b3390565b6040805180820182526002815261353960f01b6020808301919091526001600160a01b038087166000908152600683528481209187168152915291822054611840918490611b35565b6001600160a01b0380861660008181526006602090815260408083208986168085529083529281902086905580517f000000000000000000000000000000000000000000000000000000000000000090951685529084018590528051949550909391927fda919360433220e13b51e8c211e490d148e61a3bd53de8c097194e458b97f3e1929081900390910190a350505050565b604080518082019091526002815261035360f41b60208201526000908261193c5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b5060408051808201909152600280825261068760f31b60208301528304906b033b2e3c9fd0803ce80000008219048511156119b85760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b5082816b033b2e3c9fd0803ce8000000860201816119d257fe5b04949350505050565b6001600160a01b038216611a36576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b611a42600083836117d8565b600254611a4f818361164d565b6002556001600160a01b038316600090815260208190526040902054611a75818461164d565b6001600160a01b03909416600090815260208190526040902093909355505050565b6001600160a01b038216611adc5760405162461bcd60e51b8152600401808060200182810382526021815260200180611e326021913960400191505060405180910390fd5b611ae8826000836117d8565b600254611af58183611570565b6002556001600160a01b038316600090815260208181526040918290205482516060810190935260228084529092611a7592869290611def908301398391905b60008184841115611b875760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b505050900390565b60008183611bde5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b506000838581611bea57fe5b0495945050505050565b611c06826001600160a01b0316611db2565b611c57576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310611c955780518252601f199092019160209182019101611c76565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114611cf7576040519150601f19603f3d011682016040523d82523d6000602084013e611cfc565b606091505b509150915081611d53576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611dac57808060200190516020811015611d6f57600080fd5b5051611dac5760405162461bcd60e51b815260040180806020018281038252602a815260200180611e7d602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611de657508115155b94935050505056fe45524332303a206275726e20616d6f756e7420657863656564732062616c616e6365536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a206275726e2066726f6d20746865207a65726f206164647265737343616c6c6572206973206e6f742052657761726473446973747269627574696f6e20636f6e74726163745361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220c2433cb3519887cc7f8704b8163a6f50c44c9b67f69a2a557d4570715b532aaa64736f6c634300060c0033000000000000000000000000375cfb020dcda2d02aa47b4adbe94e924d30a67d0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f98400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001f4c65766572207661726961626c6520646562742062656172696e6720554e4900000000000000000000000000000000000000000000000000000000000000000464554e4900000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061021b5760003560e01c806380faa57d11610125578063c04a8a10116100ad578063d1af0c7d1161007c578063d1af0c7d146105df578063dd62ed3e146105e7578063df136d6514610615578063ebe2b12b1461061d578063f5298aca146106255761021b565b8063c04a8a101461059b578063c72c4d10146105c7578063c8f33c91146105cf578063cd3daf9d146105d75761021b565b8063a9059cbb116100f4578063a9059cbb1461051b578063b16a19de14610547578063b1bf962d1461054f578063b3f1c93d14610557578063b9a7b622146105935761021b565b806380faa57d146104e55780638b876347146104ed57806395d89b4114610513578063a457c2d7146104315761021b565b8063246132f9116101a85780633d18b912116101775780633d18b9121461045d5780636bd76d241461046557806370a08231146104935780637535d246146104b95780637b0a47ee146104dd5761021b565b8063246132f9146103e6578063313ce5671461040b578063386a95251461042957806339509351146104315761021b565b80630afbcdc9116101ef5780630afbcdc91461033b57806318160ddd1461037a5780631c1f78eb146103825780631da24f3e1461038a57806323b872dd146103b05761021b565b80628cc2621461022057806306fdde03146102585780630700037d146102d5578063095ea7b3146102fb575b600080fd5b6102466004803603602081101561023657600080fd5b50356001600160a01b0316610657565b60408051918252519081900360200190f35b6102606106c7565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561029a578181015183820152602001610282565b50505050905090810190601f1680156102c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610246600480360360208110156102eb57600080fd5b50356001600160a01b031661075e565b6103276004803603604081101561031157600080fd5b506001600160a01b038135169060200135610770565b604080519115158252519081900360200190f35b6103616004803603602081101561035157600080fd5b50356001600160a01b03166107b8565b6040805192835260208301919091528051918290030190f35b6102466107d5565b6102466108a6565b610246600480360360208110156103a057600080fd5b50356001600160a01b03166108bf565b610327600480360360608110156103c657600080fd5b506001600160a01b038135811691602081013590911690604001356108ca565b610409600480360360408110156103fc57600080fd5b5080359060200135610912565b005b610413610c17565b6040805160ff9092168252519081900360200190f35b610246610c20565b6103276004803603604081101561044757600080fd5b506001600160a01b038135169060200135610c26565b610409610c75565b6102466004803603604081101561047b57600080fd5b506001600160a01b0381358116916020013516610daf565b610246600480360360208110156104a957600080fd5b50356001600160a01b0316610ddc565b6104c1610ec3565b604080516001600160a01b039092168252519081900360200190f35b610246610ee7565b610246610eed565b6102466004803603602081101561050357600080fd5b50356001600160a01b0316610efb565b610260610f0d565b6103276004803603604081101561053157600080fd5b506001600160a01b0381351690602001356108ca565b6104c1610f6e565b610246610f92565b6103276004803603608081101561056d57600080fd5b506001600160a01b03813581169160208101359091169060408101359060600135610f9c565b61024661122a565b610409600480360360408110156105b157600080fd5b506001600160a01b03813516906020013561122f565b6104c16112e9565b6102466112f8565b6102466112fe565b6104c161134c565b610246600480360360408110156105fd57600080fd5b506001600160a01b0381358116916020013516610c26565b61024661135b565b610246611361565b6104096004803603606081101561063b57600080fd5b506001600160a01b038135169060208101359060400135611367565b6001600160a01b0381166000908152600f6020908152604080832054600e9092528220546106bf91906106b990670de0b6b3a7640000906106b3906106a49061069e6112fe565b90611570565b6106ad88610ddc565b906115b2565b9061160b565b9061164d565b90505b919050565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156107535780601f1061072857610100808354040283529160200191610753565b820191906000526020600020905b81548152906001019060200180831161073657829003601f168201915b505050505090505b90565b600f6020526000908152604090205481565b6040805162461bcd60e51b81526020600482015260166024820152751054141493d5905317d393d517d4d5541413d495115160521b6044820152905160009181900360640190fd5b6000806107c4836116a7565b6107cc6116c2565b91509150915091565b60006108a17f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b031663386497fd7f0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f9846040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561086757600080fd5b505afa15801561087b573d6000803e3d6000fd5b505050506040513d602081101561089157600080fd5b505161089b6116c2565b906116c8565b905090565b60006108a1600b54600a546115b290919063ffffffff16565b60006106bf826116a7565b6040805162461bcd60e51b81526020600482015260166024820152751514905394d1915497d393d517d4d5541413d495115160521b6044820152905160009181900360640190fd5b601060009054906101000a90046001600160a01b03166001600160a01b031663f2fbb22d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561096057600080fd5b505afa158015610974573d6000803e3d6000fd5b505050506040513d602081101561098a57600080fd5b50516001600160a01b031633146109d25760405162461bcd60e51b815260040180806020018281038252602a815260200180611e53602a913960400191505060405180910390fd5b60006109dc6112fe565b600d556109e7610eed565b600c556001600160a01b03811615610a2e57610a0281610657565b6001600160a01b0382166000908152600f6020908152604080832093909355600d54600e909152919020555b600854604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015610a7957600080fd5b505afa158015610a8d573d6000803e3d6000fd5b505050506040513d6020811015610aa357600080fd5b50516009549091504210610b3857600b839055610ac0848461160b565b600a55600b54610ad190829061160b565b600a541115610b22576040805162461bcd60e51b81526020600482015260186024820152770a0e4deecd2c8cac840e4caeec2e4c840e8dede40d0d2ced60431b604482015290519081900360640190fd5b600b54610b3090429061164d565b600955610bd2565b600954600090610b489042611570565b90506000610b61600a54836115b290919063ffffffff16565b9050610b71826106b3888461164d565b600a55610b7e838361160b565b600a541115610bcf576040805162461bcd60e51b81526020600482015260186024820152770a0e4deecd2c8cac840e4caeec2e4c840e8dede40d0d2ced60431b604482015290519081900360640190fd5b50505b42600c55604080518581526020810185905281517f6c07ee05dcf262f13abf9d87b846ee789d2f90fe991d495acd7d7fc109ee1f55929181900390910190a150505050565b60055460ff1690565b600b5481565b6040805162461bcd60e51b815260206004820152601760248201527f414c4c4f57414e43455f4e4f545f535550504f525445440000000000000000006044820152905160009181900360640190fd5b600780546001019081905533610c896112fe565b600d55610c94610eed565b600c556001600160a01b03811615610cdb57610caf81610657565b6001600160a01b0382166000908152600f6020908152604080832093909355600d54600e909152919020555b336000908152600f602052604090205480610cf557600080fd5b336000818152600f6020526040812055600854610d1e916001600160a01b039091169083611786565b60408051828152905133917fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486919081900360200190a250506007548114610dac576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b50565b6001600160a01b038083166000908152600660209081526040808320938516835292905220545b92915050565b600080610de8836116a7565b905080610df95760009150506106c2565b610ebc7f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b031663386497fd7f0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f9846040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610e8957600080fd5b505afa158015610e9d573d6000803e3d6000fd5b505050506040513d6020811015610eb357600080fd5b505182906116c8565b9392505050565b7f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee283981565b600a5481565b60006108a1426009546117dd565b600e6020526000908152604090205481565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156107535780601f1061072857610100808354040283529160200191610753565b7f0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f98481565b60006108a16116c2565b60007f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b0316610fd06117f3565b6001600160a01b03161460405180604001604052806002815260200161323960f01b8152509061107e5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561104357818101518382015260200161102b565b50505050905090810190601f1680156110705780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50836110886112fe565b600d55611093610eed565b600c556001600160a01b038116156110da576110ae81610657565b6001600160a01b0382166000908152600f6020908152604080832093909355600d54600e909152919020555b846001600160a01b0316866001600160a01b0316146110fe576110fe8587866117f7565b6000611109866116a7565b9050600061111786866118d4565b6040805180820190915260028152611a9b60f11b60208201529091508161117f5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b5061118a87826119db565b6040805187815290516001600160a01b038916916000917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a3866001600160a01b0316886001600160a01b03167f2f00e3cdd69a77be7ed215ec7b2a36784dd158f921fca79ac29deffa353fe6ee8888604051808381526020018281526020019250505060405180910390a350159695505050505050565b600181565b806006600061123c6117f3565b6001600160a01b03908116825260208083019390935260409182016000908120918716808252919093529120919091556112746117f3565b6001600160a01b03167fda919360433220e13b51e8c211e490d148e61a3bd53de8c097194e458b97f3e17f0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f9848460405180836001600160a01b031681526020018281526020019250505060405180910390a35050565b6010546001600160a01b031681565b600c5481565b60006113086107d5565b6113155750600d5461075b565b6108a16113436113236107d5565b6106b3670de0b6b3a76400006106ad600a546106ad600c5461069e610eed565b600d549061164d565b6008546001600160a01b031681565b600d5481565b60095481565b7f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b03166113996117f3565b6001600160a01b03161460405180604001604052806002815260200161323960f01b8152509061140a5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b50826114146112fe565b600d5561141f610eed565b600c556001600160a01b038116156114665761143a81610657565b6001600160a01b0382166000908152600f6020908152604080832093909355600d54600e909152919020555b600061147284846118d4565b60408051808201909152600281526106a760f31b6020820152909150816114da5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b506114e58582611a97565b6040805185815290516000916001600160a01b038816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a3604080518581526020810185905281516001600160a01b038816927f49995e5dd6158cf69ad3e9777c46755a1a826a446c6416992167462dad033b2a928290030190a25050505050565b6000610ebc83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611b35565b6000826115c157506000610dd6565b828202828482816115ce57fe5b0414610ebc5760405162461bcd60e51b8152600401808060200182810382526021815260200180611e116021913960400191505060405180910390fd5b6000610ebc83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250611b8f565b600082820183811015610ebc576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6001600160a01b031660009081526020819052604090205490565b60025490565b60008215806116d5575081155b156116e257506000610dd6565b816b019d971e4fe8401e7400000019816116f857fe5b0483111560405180604001604052806002815260200161068760f31b815250906117635760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b50506b033b2e3c9fd0803ce800000091026b019d971e4fe8401e74000000010490565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526117d8908490611bf4565b505050565b60008183106117ec5781610ebc565b5090919050565b3390565b6040805180820182526002815261353960f01b6020808301919091526001600160a01b038087166000908152600683528481209187168152915291822054611840918490611b35565b6001600160a01b0380861660008181526006602090815260408083208986168085529083529281902086905580517f0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f98490951685529084018590528051949550909391927fda919360433220e13b51e8c211e490d148e61a3bd53de8c097194e458b97f3e1929081900390910190a350505050565b604080518082019091526002815261035360f41b60208201526000908261193c5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b5060408051808201909152600280825261068760f31b60208301528304906b033b2e3c9fd0803ce80000008219048511156119b85760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b5082816b033b2e3c9fd0803ce8000000860201816119d257fe5b04949350505050565b6001600160a01b038216611a36576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b611a42600083836117d8565b600254611a4f818361164d565b6002556001600160a01b038316600090815260208190526040902054611a75818461164d565b6001600160a01b03909416600090815260208190526040902093909355505050565b6001600160a01b038216611adc5760405162461bcd60e51b8152600401808060200182810382526021815260200180611e326021913960400191505060405180910390fd5b611ae8826000836117d8565b600254611af58183611570565b6002556001600160a01b038316600090815260208181526040918290205482516060810190935260228084529092611a7592869290611def908301398391905b60008184841115611b875760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b505050900390565b60008183611bde5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561104357818101518382015260200161102b565b506000838581611bea57fe5b0495945050505050565b611c06826001600160a01b0316611db2565b611c57576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310611c955780518252601f199092019160209182019101611c76565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114611cf7576040519150601f19603f3d011682016040523d82523d6000602084013e611cfc565b606091505b509150915081611d53576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611dac57808060200190516020811015611d6f57600080fd5b5051611dac5760405162461bcd60e51b815260040180806020018281038252602a815260200180611e7d602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611de657508115155b94935050505056fe45524332303a206275726e20616d6f756e7420657863656564732062616c616e6365536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a206275726e2066726f6d20746865207a65726f206164647265737343616c6c6572206973206e6f742052657761726473446973747269627574696f6e20636f6e74726163745361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220c2433cb3519887cc7f8704b8163a6f50c44c9b67f69a2a557d4570715b532aaa64736f6c634300060c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000375cfb020dcda2d02aa47b4adbe94e924d30a67d0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f98400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001f4c65766572207661726961626c6520646562742062656172696e6720554e4900000000000000000000000000000000000000000000000000000000000000000464554e4900000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _addressesProvider (address): 0x375Cfb020DCDa2d02Aa47B4aDbe94e924d30A67d
Arg [1] : underlyingAsset (address): 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984
Arg [2] : name (string): Lever variable debt bearing UNI
Arg [3] : symbol (string): dUNI
Arg [4] : decimals (uint8): 18
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000375cfb020dcda2d02aa47b4adbe94e924d30a67d
Arg [1] : 0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f984
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [5] : 000000000000000000000000000000000000000000000000000000000000001f
Arg [6] : 4c65766572207661726961626c6520646562742062656172696e6720554e4900
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [8] : 64554e4900000000000000000000000000000000000000000000000000000000
Deployed Bytecode Sourcemap
2944:9077:57:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9701:265;;;;;;;;;;;;;;;;-1:-1:-1;9701:265:57;-1:-1:-1;;;;;9701:265:57;;:::i;:::-;;;;;;;;;;;;;;;;949:86:33;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3498:42:57;;;;;;;;;;;;;;;;-1:-1:-1;3498:42:57;-1:-1:-1;;;;;3498:42:57;;:::i;2785:161:7:-;;;;;;;;;;;;;;;;-1:-1:-1;2785:161:7;;-1:-1:-1;;;;;2785:161:7;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;8703:214:57;;;;;;;;;;;;;;;;-1:-1:-1;8703:214:57;-1:-1:-1;;;;;8703:214:57;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;7886:232;;;:::i;9974:121::-;;;:::i;7530:183::-;;;;;;;;;;;;;;;;-1:-1:-1;7530:183:57;-1:-1:-1;;;;;7530:183:57;;:::i;2952:219:7:-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;2952:219:7;;;;;;;;;;;;;;;;;:::i;10447:1400:57:-;;;;;;;;;;;;;;;;-1:-1:-1;10447:1400:57;;;;;;;:::i;:::-;;1245:86:33;;;:::i;:::-;;;;;;;;;;;;;;;;;;;3307:40:57;;;:::i;3177:203:7:-;;;;;;;;;;;;;;;;-1:-1:-1;3177:203:7;;-1:-1:-1;;;;;3177:203:7;;;;;;:::i;10103:277:57:-;;;:::i;2070:178:7:-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;2070:178:7;;;;;;;;;;:::i;5043:415:57:-;;;;;;;;;;;;;;;;-1:-1:-1;5043:415:57;-1:-1:-1;;;;;5043:415:57;;:::i;481:33:7:-;;;:::i;:::-;;;;-1:-1:-1;;;;;481:33:7;;;;;;;;;;;;;;3271:29:57;;;:::i;9134:131::-;;;:::i;3434:57::-;;;;;;;;;;;;;;;;-1:-1:-1;3434:57:57;-1:-1:-1;;;;;3434:57:57;;:::i;1094:90:33:-;;;:::i;2409:166:7:-;;;;;;;;;;;;;;;;-1:-1:-1;2409:166:7;;-1:-1:-1;;;;;2409:166:7;;;;;;:::i;427:49::-;;;:::i;8285:171:57:-;;;:::i;5989:695::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;5989:695:57;;;;;;;;;;;;;;;;;;;;;;:::i;3100:49::-;;;:::i;1611:239:7:-;;;;;;;;;;;;;;;;-1:-1:-1;1611:239:7;;-1:-1:-1;;;;;1611:239:7;;;;;;:::i;3547:53:57:-;;;:::i;3354:29::-;;;:::i;9273:420::-;;;:::i;3200:26::-;;;:::i;2581:198:7:-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;2581:198:7;;;;;;;;;;:::i;3390:35:57:-;;;:::i;3233:31::-;;;:::i;6957:404::-;;;;;;;;;;;;;;;;-1:-1:-1;6957:404:57;;-1:-1:-1;;;;;6957:404:57;;;;;;;;;;;:::i;9701:265::-;-1:-1:-1;;;;;9941:16:57;;9755:7;9941:16;;;:7;:16;;;;;;;;;9857:22;:31;;;;;;9795:163;;9941:16;9795:123;;9913:4;;9795:95;;9836:53;;:16;:14;:16::i;:::-;:20;;:53::i;:::-;9795:18;9805:7;9795:9;:18::i;:::-;:40;;:95::i;:::-;:117;;:123::i;:::-;:145;;:163::i;:::-;9775:183;;9701:265;;;;:::o;949:86:33:-;1024:5;1017:12;;;;;;;;;;;;;-1:-1:-1;;1017:12:33;;;;;;;;;;;;;;;;;;;;;;;;;;995:13;;1017:12;;1024:5;;1017:12;;;1024:5;1017:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;949:86;;:::o;3498:42:57:-;;;;;;;;;;;;;:::o;2785:161:7:-;2908:32;;;-1:-1:-1;;;2908:32:7;;;;;;;;;;;;-1:-1:-1;;;2908:32:7;;;;;;-1:-1:-1;;2908:32:7;;;;;;;8703:214:57;8824:7;8833;8866:21;8882:4;8866:15;:21::i;:::-;8889:19;:17;:19::i;:::-;8858:51;;;;8703:214;;;:::o;7886:232::-;7947:7;7987:123;8032:4;-1:-1:-1;;;;;8032:37:57;;8070:24;8032:63;;;;;;;;;;;;;-1:-1:-1;;;;;8032:63:57;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;8032:63:57;7987:19;:17;:19::i;:::-;:26;;:123::i;:::-;7967:143;;7886:232;:::o;9974:121::-;10029:7;10056:31;10071:15;;10056:10;;:14;;:31;;;;:::i;7530:183::-;7652:7;7684:21;7700:4;7684:15;:21::i;2952:219:7:-;3133:32;;;-1:-1:-1;;;3133:32:7;;;;;;;;;;;;-1:-1:-1;;;3133:32:7;;;;;;-1:-1:-1;;3133:32:7;;;;;;;10447:1400:57;4772:17;;:42;;;-1:-1:-1;;;4772:42:57;;;;-1:-1:-1;;;;;4772:17:57;;;;:40;;:42;;;;;;;;;;;;;;;:17;:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4772:42:57;-1:-1:-1;;;;;4758:56:57;:10;:56;4736:148;;;;-1:-1:-1;;;4736:148:57;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10598:1:::1;4425:16;:14;:16::i;:::-;4402:20;:39:::0;4469:26:::1;:24;:26::i;:::-;4452:14;:43:::0;-1:-1:-1;;;;;4510:21:57;::::1;::::0;4506:157:::1;;4567:15;4574:7;4567:6;:15::i;:::-;-1:-1:-1::0;;;;;4548:16:57;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4631:20:::1;::::0;4597:22:::1;:31:::0;;;;;;:54;4506:157:::1;10985:12:::2;::::0;:37:::2;::::0;;-1:-1:-1;;;10985:37:57;;11016:4:::2;10985:37;::::0;::::2;::::0;;;-1:-1:-1;;;;;;;10985:12:57::2;::::0;-1:-1:-1;;10985:37:57;;;;;::::2;::::0;;;;;;;;:12;:37;::::2;;::::0;::::2;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;::::0;::::2;;-1:-1:-1::0;10985:37:57;11056:12:::2;::::0;10985:37;;-1:-1:-1;11037:15:57::2;:31;11033:707;;11085:15;:34:::0;;;11147:27:::2;:6:::0;11103:16;11147:10:::2;:27::i;:::-;11134:10;:40:::0;11241:15:::2;::::0;11229:28:::2;::::0;:7;;:11:::2;:28::i;:::-;11215:10;;:42;;11189:128;;;::::0;;-1:-1:-1;;;11189:128:57;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;11189:128:57;;;;;;;;;;;;;::::2;;11367:15;::::0;11347:36:::2;::::0;:15:::2;::::0;:19:::2;:36::i;:::-;11332:12;:51:::0;11033:707:::2;;;11436:12;::::0;11416:17:::2;::::0;11436:33:::2;::::0;11453:15:::2;11436:16;:33::i;:::-;11416:53;;11484:16;11503:25;11517:10;;11503:9;:13;;:25;;;;:::i;:::-;11484:44:::0;-1:-1:-1;11556:35:57::2;11581:9:::0;11556:20:::2;:6:::0;11484:44;11556:10:::2;:20::i;:35::-;11543:10;:48:::0;11646:22:::2;:7:::0;11658:9;11646:11:::2;:22::i;:::-;11632:10;;:36;;11606:122;;;::::0;;-1:-1:-1;;;11606:122:57;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;11606:122:57;;;;;;;;;;;;;::::2;;11033:707;;;11771:15;11754:14;:32:::0;11802:37:::2;::::0;;;;;::::2;::::0;::::2;::::0;;;;;::::2;::::0;;;;;;;;;::::2;4673:1;4895::::1;10447:1400:::0;;:::o;1245:86:33:-;1316:9;;;;1245:86;:::o;3307:40:57:-;;;;:::o;3177:203:7:-;3341:33;;;-1:-1:-1;;;3341:33:7;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3341:33:7;;;;;;;10103:277:57;2560:13;:18;;2577:1;2560:18;;;;;10157:10:::1;4425:16;:14;:16::i;:::-;4402:20;:39:::0;4469:26:::1;:24;:26::i;:::-;4452:14;:43:::0;-1:-1:-1;;;;;4510:21:57;::::1;::::0;4506:157:::1;;4567:15;4574:7;4567:6;:15::i;:::-;-1:-1:-1::0;;;;;4548:16:57;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4631:20:::1;::::0;4597:22:::1;:31:::0;;;;;;:54;4506:157:::1;10203:10:::2;10178:14;10195:19:::0;;;:7:::2;:19;::::0;;;;;10231:10;10223:19:::2;;;::::0;::::2;;10259:10;10273:1;10251:19:::0;;;:7:::2;:19;::::0;;;;:23;10283:12:::2;::::0;:45:::2;::::0;-1:-1:-1;;;;;10283:12:57;;::::2;::::0;10321:6;10283:25:::2;:45::i;:::-;10342:30;::::0;;;;;;;10353:10:::2;::::0;10342:30:::2;::::0;;;;;::::2;::::0;;::::2;4673:1;2636::::1;2686:13:::0;;2670:12;:29;2648:110;;;;;-1:-1:-1;;;2648:110:57;;;;;;;;;;;;;;;;;;;;;;;;;;;;10103:277;:::o;2070:178:7:-;-1:-1:-1;;;;;2207:27:7;;;2181:7;2207:27;;;:17;:27;;;;;;;;:35;;;;;;;;;;2070:178;;;;;:::o;5043:415:57:-;5159:7;5184:21;5208;5224:4;5208:15;:21::i;:::-;5184:45;-1:-1:-1;5246:18:57;5242:59;;5288:1;5281:8;;;;;5242:59;5372:63;;;-1:-1:-1;;;5372:63:57;;-1:-1:-1;;;;;5410:24:57;5372:63;;;;;;;;5333:117;;5372:4;:37;;;;:63;;;;;;;;;;;;;;:37;:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5372:63:57;5333:13;;:20;:117::i;:::-;5313:137;5043:415;-1:-1:-1;;;5043:415:57:o;481:33:7:-;;;:::o;3271:29:57:-;;;;:::o;9134:131::-;9191:7;9218:39;9227:15;9244:12;;9218:8;:39::i;3434:57::-;;;;;;;;;;;;;:::o;1094:90:33:-;1171:7;1164:14;;;;;;;;;;;;;-1:-1:-1;;1164:14:33;;;;;;;;;;;;;;;;;;;;;;;;;;1142:13;;1164:14;;1171:7;;1164:14;;;1171:7;1164:14;;;;;;;;;;;;;;;;;;;;;;;;427:49:7;;;:::o;8285:171:57:-;8397:7;8429:19;:17;:19::i;5989:695::-;6178:4;-1:-1:-1;;;;;751:4:7;727:29;:12;:10;:12::i;:::-;758:36;;;;;;;;;;;;-1:-1:-1;;;758:36:7;;;;;-1:-1:-1;;;;;727:29:7;;;;;719:76;;;;-1:-1:-1;;;719:76:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6157:10:57::1;4425:16;:14;:16::i;:::-;4402:20;:39:::0;4469:26:::1;:24;:26::i;:::-;4452:14;:43:::0;-1:-1:-1;;;;;4510:21:57;::::1;::::0;4506:157:::1;;4567:15;4574:7;4567:6;:15::i;:::-;-1:-1:-1::0;;;;;4548:16:57;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4631:20:::1;::::0;4597:22:::1;:31:::0;;;;;;:54;4506:157:::1;-1:-1:-1::0;;;;;6199:18:57;;::::2;::::0;;::::2;;6195:101;;6234:50;6259:10;6271:4;6277:6;6234:24;:50::i;:::-;6308:23;6334:27;6350:10;6334:15;:27::i;:::-;6308:53:::0;-1:-1:-1;6372:20:57::2;6395;:6:::0;6409:5;6395:13:::2;:20::i;:::-;6453:29;::::0;;;;::::2;::::0;;;::::2;::::0;;-1:-1:-1;;;6453:29:57::2;::::0;::::2;::::0;6372:43;;-1:-1:-1;6372:43:57;6426:57:::2;;;::::0;-1:-1:-1;;;6426:57:57;;::::2;;::::0;::::2;::::0;;;;;;;;;;;;;;;;;;;;;;::::2;::::0;;;;-1:-1:-1;6426:57:57;;::::2;;::::0;;::::2;::::0;;;::::2;::::0;::::2;;;;;;6496:31;6502:10;6514:12;6496:5;:31::i;:::-;6543:40;::::0;;;;;;;-1:-1:-1;;;;;6543:40:57;::::2;::::0;6560:1:::2;::::0;6543:40:::2;::::0;;;;::::2;::::0;;::::2;6599:37;::::0;;;;;::::2;::::0;::::2;::::0;;;;;-1:-1:-1;;;;;6599:37:57;;::::2;::::0;;;::::2;::::0;::::2;::::0;;;;;;;;;::::2;-1:-1:-1::0;6656:20:57;;5989:695;-1:-1:-1;;;;;;5989:695:57:o;3100:49::-;3146:3;3100:49;:::o;1611:239:7:-;1743:6;1698:17;:31;1716:12;:10;:12::i;:::-;-1:-1:-1;;;;;1698:31:7;;;;;;;;;;;;;;;;;-1:-1:-1;1698:31:7;;;:42;;;;;;;;;;;;:51;;;;1786:12;:10;:12::i;:::-;1761:83;;;-1:-1:-1;;;;;1811:24:7;1761:83;;;;;;;;;;;;;;;;;;;;;;;;;;1611:239;;:::o;3547:53:57:-;;;-1:-1:-1;;;;;3547:53:57;;:::o;3354:29::-;;;;:::o;9273:420::-;9320:7;9344:13;:11;:13::i;:::-;9340:78;;-1:-1:-1;9386:20:57;;9379:27;;9340:78;9448:237;9491:179;9656:13;:11;:13::i;:::-;9491:138;9624:4;9491:106;9586:10;;9491:68;9544:14;;9491:26;:24;:26::i;:179::-;9448:20;;;:24;:237::i;3200:26::-;;;-1:-1:-1;;;;;3200:26:57;;:::o;3390:35::-;;;;:::o;3233:31::-;;;;:::o;6957:404::-;-1:-1:-1;;;;;751:4:7;727:29;:12;:10;:12::i;:::-;758:36;;;;;;;;;;;;-1:-1:-1;;;758:36:7;;;;;-1:-1:-1;;;;;727:29:7;;;;;719:76;;;;-1:-1:-1;;;719:76:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;719:76:7;;;;;;;;;;;;;;;;;;7096:4:57::1;4425:16;:14;:16::i;:::-;4402:20;:39:::0;4469:26:::1;:24;:26::i;:::-;4452:14;:43:::0;-1:-1:-1;;;;;4510:21:57;::::1;::::0;4506:157:::1;;4567:15;4574:7;4567:6;:15::i;:::-;-1:-1:-1::0;;;;;4548:16:57;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4631:20:::1;::::0;4597:22:::1;:31:::0;;;;;;:54;4506:157:::1;7113:20:::2;7136;:6:::0;7150:5;7136:13:::2;:20::i;:::-;7194:29;::::0;;;;::::2;::::0;;;::::2;::::0;;-1:-1:-1;;;7194:29:57::2;::::0;::::2;::::0;7113:43;;-1:-1:-1;7113:43:57;7167:57:::2;;;::::0;-1:-1:-1;;;7167:57:57;;::::2;;::::0;::::2;::::0;;;;;;;;;;;;;;;;;;;;;;::::2;::::0;;;;-1:-1:-1;7167:57:57;;::::2;;::::0;;::::2;::::0;;;::::2;::::0;::::2;;;;;;7237:25;7243:4;7249:12;7237:5;:25::i;:::-;7278:34;::::0;;;;;;;7301:1:::2;::::0;-1:-1:-1;;;;;7278:34:57;::::2;::::0;::::2;::::0;;;;::::2;::::0;;::::2;7328:25;::::0;;;;;::::2;::::0;::::2;::::0;;;;;-1:-1:-1;;;;;7328:25:57;::::2;::::0;::::2;::::0;;;;;;::::2;4673:1;802::7::1;6957:404:57::0;;;:::o;1299:130:52:-;1357:7;1380:43;1384:1;1387;1380:43;;;;;;;;;;;;;;;;;:3;:43::i;2133:431::-;2191:7;2420:6;2416:37;;-1:-1:-1;2444:1:52;2437:8;;2416:37;2473:5;;;2477:1;2473;:5;:1;2493:5;;;;;:10;2485:56;;;;-1:-1:-1;;;2485:56:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3008:126;3066:7;3089:39;3093:1;3096;3089:39;;;;;;;;;;;;;;;;;:3;:39::i;877:167::-;935:7;963:5;;;983:6;;;;975:46;;;;;-1:-1:-1;;;975:46:52;;;;;;;;;;;;;;;;;;;;;;;;;;;1558:121:33;-1:-1:-1;;;;;1655:18:33;1632:7;1655:18;;;;;;;;;;;;1558:121::o;1396:102::-;1480:12;;1396:102;:::o;2086:261:61:-;2147:7;2167:6;;;:16;;-1:-1:-1;2177:6:61;;2167:16;2163:47;;;-1:-1:-1;2201:1:61;2194:8;;2163:47;2263:1;-1:-1:-1;;2263:1:61;2231:33;;;;2266:35;;;;;;;;;;;;-1:-1:-1;;;2266:35:61;;;;;2231:33;;2226:38;;;2218:84;;;;-1:-1:-1;;;2218:84:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2218:84:61;;;;;;;;;;;;;;;;;-1:-1:-1;;439:4:61;2319:5;;484:7;2319:15;2318:23;;2086:261::o;737:190:51:-;862:58;;;-1:-1:-1;;;;;862:58:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;862:58:51;-1:-1:-1;;;862:58:51;;;836:85;;855:5;;836:18;:85::i;:::-;737:190;;;:::o;826:106:57:-;884:7;915:1;911;:5;:13;;923:1;911:13;;;-1:-1:-1;919:1:57;;826:106;-1:-1:-1;826:106:57:o;601:100:5:-;685:10;601:100;:::o;3605:410:7:-;3811:34;;;;;;;;;;;-1:-1:-1;;;3811:34:7;;;;;;;;-1:-1:-1;;;;;3759:28:7;;;-1:-1:-1;3759:28:7;;;:17;:28;;;;;:39;;;;;;;;;;;:87;;3803:6;;3759:43;:87::i;:::-;-1:-1:-1;;;;;3855:28:7;;;;;;;:17;:28;;;;;;;;:39;;;;;;;;;;;;;:54;;;3923:86;;3970:24;3923:86;;;;;;;;;;;;;3855:54;;-1:-1:-1;3855:39:7;;:28;;3923:86;;;;;;;;;;;3605:410;;;;:::o;2510:286:61:-;2603:28;;;;;;;;;;;;-1:-1:-1;;;2603:28:61;;;;-1:-1:-1;;2595:6:61;2587:45;;;;-1:-1:-1;;;2587:45:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2587:45:61;;;;;;;;;;;;;;;;;-1:-1:-1;2717:35:61;;;;;;;;;2659:1;2717:35;;;-1:-1:-1;;;2717:35:61;;;;2655:5;;;439:4;2683:25;;2682:33;2677:38;;;2669:84;;;;-1:-1:-1;;;2669:84:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2669:84:61;;;;;;;;;;;;;;;;;;2789:1;2780:5;439:4;2770:1;:7;:15;2769:21;;;;;;;2510:286;-1:-1:-1;;;;2510:286:61:o;5302:407:33:-;-1:-1:-1;;;;;5382:21:33;;5374:65;;;;;-1:-1:-1;;;5374:65:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;5448:49;5477:1;5481:7;5490:6;5448:20;:49::i;:::-;5531:12;;5565:26;5531:12;5584:6;5565:18;:26::i;:::-;5550:12;:41;-1:-1:-1;;;;;5628:18:33;;5600:25;5628:18;;;;;;;;;;;5674:29;5628:18;5696:6;5674:21;:29::i;:::-;-1:-1:-1;;;;;5653:18:33;;;;:9;:18;;;;;;;;;;:50;;;;-1:-1:-1;;;5302:407:33:o;5715:449::-;-1:-1:-1;;;;;5795:21:33;;5787:67;;;;-1:-1:-1;;;5787:67:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5863:49;5884:7;5901:1;5905:6;5863:20;:49::i;:::-;5946:12;;5980:26;5946:12;5999:6;5980:18;:26::i;:::-;5965:12;:41;-1:-1:-1;;;;;6043:18:33;;6015:25;6043:18;;;;;;;;;;;;;6089:67;;;;;;;;;;;;6043:18;;6089:67;;6111:6;;6089:67;;;;;;:17;;:67;1704:198:52;1810:7;1842:12;1834:6;;;;1826:29;;;;-1:-1:-1;;;1826:29:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1826:29:52;;;;;;;;;;;;;;;;;-1:-1:-1;;;1874:5:52;;;1704:198::o;3598:343::-;3704:7;3798:12;3791:5;3783:28;;;;-1:-1:-1;;;3783:28:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;3783:28:52;;;;;;;;;;;;;;;;;;3818:9;3834:1;3830;:5;;;;;;;3598:343;-1:-1:-1;;;;;3598:343:52:o;1523:567:51:-;1607:27;-1:-1:-1;;;;;1607:25:51;;;:27::i;:::-;1599:71;;;;;-1:-1:-1;;;1599:71:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;1736:12;1750:23;1785:5;-1:-1:-1;;;;;1777:19:51;1797:4;1777:25;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1777:25:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1735:67;;;;1817:7;1809:52;;;;;-1:-1:-1;;;1809:52:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1874:17;;:21;1870:215;;2011:10;2000:30;;;;;;;;;;;;;;;-1:-1:-1;2000:30:51;1992:85;;;;-1:-1:-1;;;1992:85:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1523:567;;;;:::o;710:597:0:-;770:4;1217:20;;1061:66;1258:23;;;;;;:42;;-1:-1:-1;1285:15:0;;;1258:42;1250:51;710:597;-1:-1:-1;;;;710:597:0:o
Swarm Source
://c2433cb3519887cc7f8704b8163a6f50c44c9b67f69a2a557d4570715b532aaa
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.