Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 27 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Get Reward | 12689724 | 1165 days ago | IN | 0 ETH | 0.00089596 | ||||
Get Reward | 12617593 | 1176 days ago | IN | 0 ETH | 0.00080636 | ||||
Get Reward | 12605839 | 1178 days ago | IN | 0 ETH | 0.00112815 | ||||
Get Reward | 12600019 | 1179 days ago | IN | 0 ETH | 0.0008942 | ||||
Get Reward | 12593634 | 1180 days ago | IN | 0 ETH | 0.00156606 | ||||
Get Reward | 12587753 | 1180 days ago | IN | 0 ETH | 0.00176395 | ||||
Get Reward | 12587578 | 1181 days ago | IN | 0 ETH | 0.00154345 | ||||
Get Reward | 12582323 | 1181 days ago | IN | 0 ETH | 0.00178285 | ||||
Get Reward | 12577832 | 1182 days ago | IN | 0 ETH | 0.00121271 | ||||
Get Reward | 12568763 | 1183 days ago | IN | 0 ETH | 0.00209469 | ||||
Get Reward | 12561766 | 1184 days ago | IN | 0 ETH | 0.00351687 | ||||
Get Reward | 12557418 | 1185 days ago | IN | 0 ETH | 0.00275617 | ||||
Get Reward | 12554882 | 1186 days ago | IN | 0 ETH | 0.00220494 | ||||
Get Reward | 12548204 | 1187 days ago | IN | 0 ETH | 0.00220494 | ||||
Get Reward | 12541220 | 1188 days ago | IN | 0 ETH | 0.00188522 | ||||
Get Reward | 12539574 | 1188 days ago | IN | 0 ETH | 0.00234901 | ||||
Get Reward | 12535559 | 1189 days ago | IN | 0 ETH | 0.00198444 | ||||
Get Reward | 12532175 | 1189 days ago | IN | 0 ETH | 0.00220494 | ||||
Get Reward | 12525569 | 1190 days ago | IN | 0 ETH | 0.00256125 | ||||
Get Reward | 12515899 | 1192 days ago | IN | 0 ETH | 0.00253592 | ||||
Get Reward | 12514749 | 1192 days ago | IN | 0 ETH | 0.00260759 | ||||
Get Reward | 12501085 | 1194 days ago | IN | 0 ETH | 0.00542498 | ||||
Get Reward | 12488948 | 1196 days ago | IN | 0 ETH | 0.00363815 | ||||
Get Reward | 12476261 | 1198 days ago | IN | 0 ETH | 0.0054021 | ||||
Get Reward | 12470426 | 1199 days ago | IN | 0 ETH | 0.00891429 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
12456552 | 1201 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
XToken
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 {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; /** * @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; 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; 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; 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":"underlyingAssetAddress","type":"address"},{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","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":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"BalanceTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","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":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":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_REVISION","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL","outputs":[{"internalType":"contract IMarginPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESERVE_TREASURY_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UINT_MAX_VALUE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNDERLYING_ASSET_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"receiverOfUnderlying","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":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"mintToTreasury","outputs":[],"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":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","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":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferOnLiquidation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferUnderlyingTo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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
60e06040526000600855600060095562278d00600a553480156200002257600080fd5b506040516200321738038062003217833981810160405260a08110156200004857600080fd5b815160208301516040808501805191519395929483019291846401000000008211156200007457600080fd5b9083019060208201858111156200008a57600080fd5b8251640100000000811182820188101715620000a557600080fd5b82525081516020918201929091019080838360005b83811015620000d4578181015183820152602001620000ba565b50505050905090810190601f168015620001025780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200012657600080fd5b9083019060208201858111156200013c57600080fd5b82516401000000008111828201881017156200015757600080fd5b82525081516020918201929091019080838360005b83811015620001865781810151838201526020016200016c565b50505050905090810190601f168015620001b45780820380516001836020036101000a031916815260200191505b50604052602090810151855190935085925084918491620001dc9160039190860190620003d3565b508151620001f2906004906020850190620003d3565b506005805460ff191660ff9290921691909117905550506001600655600f80546001600160a01b0319166001600160a01b03878116919091179182905560408051633aa431a160e11b815290519290911691637548634291600480820192602092909190829003018186803b1580156200026b57600080fd5b505afa15801562000280573d6000803e3d6000fd5b505050506040513d60208110156200029757600080fd5b50516001600160601b0319606091821b811660c0529085901b16608052600f5460408051633800918160e21b815290516001600160a01b03929092169163e002460491600480820192602092909190829003018186803b158015620002fb57600080fd5b505afa15801562000310573d6000803e3d6000fd5b505050506040513d60208110156200032757600080fd5b505160601b6001600160601b03191660a052604080516376083b5b60e01b815290516001600160a01b038716916376083b5b916004808301926020929190829003018186803b1580156200037a57600080fd5b505afa1580156200038f573d6000803e3d6000fd5b505050506040513d6020811015620003a657600080fd5b5051600780546001600160a01b0319166001600160a01b03909216919091179055506200046f9350505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200041657805160ff191683800117855562000446565b8280016001018555821562000446579182015b828111156200044657825182559160200191906001019062000429565b506200045492915062000458565b5090565b5b8082111562000454576000815560010162000459565b60805160601c60a05160601c60c05160601c612d1362000504600039806108e75280610b4b52806111c252806112a5528061136d52806113b45280611a315280611cb4528061255452806126b052508061145852806114e052806115215280611577528061170e525080610b7a528061127152806112d452806117325280611bca528061258352806126615250612d136000f3fe608060405234801561001057600080fd5b50600436106102685760003560e01c80637b0a47ee11610151578063c72c4d10116100c3578063d505accf11610087578063d505accf14610691578063d7020d0a146106e2578063dd62ed3e1461071e578063df136d651461074c578063ebe2b12b14610754578063f866c3191461075c57610268565b8063c72c4d1014610669578063c8f33c9114610671578063cd3daf9d14610679578063d0fc81d214610681578063d1af0c7d1461068957610268565b8063a457c2d711610115578063a457c2d7146105d3578063a9059cbb146105ff578063ae1673351461062b578063b16a19de14610633578063b1bf962d1461063b578063b9844d8d1461064357610268565b80637b0a47ee146105725780637df5bd3b1461057a57806380faa57d1461059d5780638b876347146105a557806395d89b41146105cb57610268565b8063246132f9116101ea57806339509351116101ae57806339509351146104c05780633d18b912146104ec5780634efecaa5146104f457806370a08231146105205780637535d24614610546578063781603761461056a57610268565b8063246132f91461046557806330adf81f1461048a578063313ce567146104925780633644e515146104b0578063386a9525146104b857610268565b8063156e29f611610231578063156e29f6146103c757806318160ddd146103f95780631c1f78eb146104015780631da24f3e1461040957806323b872dd1461042f57610268565b80628cc2621461026d57806306fdde03146102a55780630700037d14610322578063095ea7b3146103485780630afbcdc914610388575b600080fd5b6102936004803603602081101561028357600080fd5b50356001600160a01b0316610792565b60408051918252519081900360200190f35b6102ad610800565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102e75781810151838201526020016102cf565b50505050905090810190601f1680156103145780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102936004803603602081101561033857600080fd5b50356001600160a01b0316610897565b6103746004803603604081101561035e57600080fd5b506001600160a01b0381351690602001356108a9565b604080519115158252519081900360200190f35b6103ae6004803603602081101561039e57600080fd5b50356001600160a01b03166108c6565b6040805192835260208301919091528051918290030190f35b610374600480360360608110156103dd57600080fd5b506001600160a01b0381351690602081013590604001356108e3565b610293610b2a565b610293610c0f565b6102936004803603602081101561041f57600080fd5b50356001600160a01b0316610c2d565b6103746004803603606081101561044557600080fd5b506001600160a01b03813581169160208101359091169060400135610c38565b6104886004803603604081101561047b57600080fd5b5080359060200135610cf8565b005b610293610ffd565b61049a611021565b6040805160ff9092168252519081900360200190f35b61029361102a565b610293611030565b610374600480360360408110156104d657600080fd5b506001600160a01b038135169060200135611036565b610488611084565b6102936004803603604081101561050a57600080fd5b506001600160a01b0381351690602001356111be565b6102936004803603602081101561053657600080fd5b50356001600160a01b031661129e565b61054e61136b565b604080516001600160a01b039092168252519081900360200190f35b6102ad61138f565b6102936113ac565b6104886004803603604081101561059057600080fd5b50803590602001356113b2565b6102936115c6565b610293600480360360208110156105bb57600080fd5b50356001600160a01b03166115d4565b6102ad6115e6565b610374600480360360408110156105e957600080fd5b506001600160a01b038135169060200135611647565b6103746004803603604081101561061557600080fd5b506001600160a01b0381351690602001356116af565b61054e61170c565b61054e611730565b610293611754565b6102936004803603602081101561065957600080fd5b50356001600160a01b031661175e565b61054e611770565b61029361177f565b610293611785565b6102936117d3565b61054e6117d9565b610488600480360360e08110156106a757600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c001356117e8565b610488600480360360808110156106f857600080fd5b506001600160a01b03813581169160208101359091169060408101359060600135611a2f565b6102936004803603604081101561073457600080fd5b506001600160a01b0381358116916020013516611c7b565b610293611ca6565b610293611cac565b6104886004803603606081101561077257600080fd5b506001600160a01b03813581169160208101359091169060400135611cb2565b6001600160a01b0381166000908152600e6020908152604080832054600d9092528220546107fa91906107f490670de0b6b3a7640000906107ee906107df906107d9611785565b90611e59565b6107e88861129e565b90611ea2565b90611efb565b90611f3d565b92915050565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561088c5780601f106108615761010080835404028352916020019161088c565b820191906000526020600020905b81548152906001019060200180831161086f57829003601f168201915b505050505090505b90565b600e6020526000908152604090205481565b60006108bd6108b6611f97565b8484611f9b565b50600192915050565b6000806108d283612087565b6108da6120a2565b91509150915091565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610917611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b815250906109c55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561098a578181015183820152602001610972565b50505050905090810190601f1680156109b75780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50836109cf611785565b600c556109da6115c6565b600b556001600160a01b03811615610a21576109f581610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b6000610a2c86612087565b90506000610a3a86866120a8565b6040805180820190915260028152611a9b60f11b602082015290915081610aa25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b50610aad87826121af565b6040805187815290516001600160a01b03891691600091600080516020612bdb8339815191529181900360200190a3604080518781526020810187905281516001600160a01b038a16927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a2501595945050505050565b600080610b356120a2565b905080610b46576000915050610894565b610c097f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d15e00537f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610bd657600080fd5b505afa158015610bea573d6000803e3d6000fd5b505050506040513d6020811015610c0057600080fd5b5051829061226b565b91505090565b6000610c28600a54600954611ea290919063ffffffff16565b905090565b60006107fa82612087565b6000610c45848484612329565b610cb584610c51611f97565b610cb085604051806060016040528060288152602001612bb3602891396001600160a01b038a16600090815260016020526040812090610c8f611f97565b6001600160a01b031681526020810191909152604001600020549190612336565b611f9b565b826001600160a01b0316846001600160a01b0316600080516020612bdb833981519152846040518082815260200191505060405180910390a35060019392505050565b600f60009054906101000a90046001600160a01b03166001600160a01b031663f2fbb22d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610d4657600080fd5b505afa158015610d5a573d6000803e3d6000fd5b505050506040513d6020811015610d7057600080fd5b50516001600160a01b03163314610db85760405162461bcd60e51b815260040180806020018281038252602a815260200180612c65602a913960400191505060405180910390fd5b6000610dc2611785565b600c55610dcd6115c6565b600b556001600160a01b03811615610e1457610de881610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b600754604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015610e5f57600080fd5b505afa158015610e73573d6000803e3d6000fd5b505050506040513d6020811015610e8957600080fd5b50516008549091504210610f1e57600a839055610ea68484611efb565b600955600a54610eb7908290611efb565b6009541115610f08576040805162461bcd60e51b81526020600482015260186024820152770a0e4deecd2c8cac840e4caeec2e4c840e8dede40d0d2ced60431b604482015290519081900360640190fd5b600a54610f16904290611f3d565b600855610fb8565b600854600090610f2e9042611e59565b90506000610f4760095483611ea290919063ffffffff16565b9050610f57826107ee8884611f3d565b600955610f648383611efb565b6009541115610fb5576040805162461bcd60e51b81526020600482015260186024820152770a0e4deecd2c8cac840e4caeec2e4c840e8dede40d0d2ced60431b604482015290519081900360640190fd5b50505b42600b55604080518581526020810185905281517f6c07ee05dcf262f13abf9d87b846ee789d2f90fe991d495acd7d7fc109ee1f55929181900390910190a150505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60055460ff1690565b60115481565b600a5481565b60006108bd611043611f97565b84610cb08560016000611054611f97565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490611f3d565b600680546001019081905533611098611785565b600c556110a36115c6565b600b556001600160a01b038116156110ea576110be81610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b336000908152600e60205260409020548061110457600080fd5b336000818152600e602052604081205560075461112d916001600160a01b039091169083612390565b60408051828152905133917fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486919081900360200190a2505060065481146111bb576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b50565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166111f2611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b815250906112635760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b506112986001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168484612390565b50919050565b60006107fa7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d15e00537f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561133057600080fd5b505afa158015611344573d6000803e3d6000fd5b505050506040513d602081101561135a57600080fd5b505161136584612087565b9061226b565b7f000000000000000000000000000000000000000000000000000000000000000081565b604051806040016040528060018152602001603160f81b81525081565b60095481565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166113e4611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b815250906114555760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b507f000000000000000000000000000000000000000000000000000000000000000061147f611785565b600c5561148a6115c6565b600b556001600160a01b038116156114d1576114a581610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b826114db576115c1565b61150e7f000000000000000000000000000000000000000000000000000000000000000061150985856120a8565b6121af565b6040805184815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691600091600080516020612bdb8339815191529181900360200190a3604080518481526020810184905281516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a25b505050565b6000610c28426008546123e2565b600d6020526000908152604090205481565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561088c5780601f106108615761010080835404028352916020019161088c565b60006108bd611654611f97565b84610cb085604051806060016040528060258152602001612cb9602591396001600061167e611f97565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190612336565b60006116c36116bc611f97565b8484612329565b826001600160a01b03166116d5611f97565b6001600160a01b0316600080516020612bdb833981519152846040518082815260200191505060405180910390a350600192915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610c286120a2565b60106020526000908152604090205481565b600f546001600160a01b031681565b600b5481565b600061178f610b2a565b61179c5750600c54610894565b610c286117ca6117aa610b2a565b6107ee670de0b6b3a76400006107e86009546107e8600b546107d96115c6565b600c5490611f3d565b60001981565b6007546001600160a01b031681565b6001600160a01b038716611833576040805162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22fa7aba722a960991b604482015290519081900360640190fd5b8342111561187d576040805162461bcd60e51b815260206004820152601260248201527124a72b20a624a22fa2ac2824a920aa24a7a760711b604482015290519081900360640190fd5b6001600160a01b0380881660008181526010602090815260408083205460115482517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98186015280840196909652958c166060860152608085018b905260a0850181905260c08086018b90528251808703909101815260e08601835280519084012061190160f01b6101008701526101028601969096526101228086019690965281518086039096018652610142850180835286519684019690962093909552610162840180825283905260ff88166101828501526101a284018790526101c284018690525191926001926101e28083019392601f198301929081900390910190855afa158015611992573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b0316146119f5576040805162461bcd60e51b8152602060048201526011602482015270494e56414c49445f5349474e415455524560781b604482015290519081900360640190fd5b611a00826001611f3d565b6001600160a01b038a16600090815260106020526040902055611a24898989611f9b565b505050505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611a61611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b81525090611ad25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5083611adc611785565b600c55611ae76115c6565b600b556001600160a01b03811615611b2e57611b0281610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b6000611b3a84846120a8565b60408051808201909152600281526106a760f31b602082015290915081611ba25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b50611bad86826123f8565b6001600160a01b0385163014611bf157611bf16001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168686612390565b6040805185815290516000916001600160a01b03891691600080516020612bdb8339815191529181900360200190a3846001600160a01b0316866001600160a01b03167f5d624aa9c148153ab3446c1b154f660ee7701e549fe9b62dab7171b1c80e6fa28686604051808381526020018281526020019250505060405180910390a3505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600c5481565b60085481565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611ce4611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b81525090611d555760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5082611d5f611785565b600c55611d6a6115c6565b600b556001600160a01b03811615611db157611d8581610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b82611dba611785565b600c55611dc56115c6565b600b556001600160a01b03811615611e0c57611de081610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b611e19858585600061249a565b836001600160a01b0316856001600160a01b0316600080516020612bdb833981519152856040518082815260200191505060405180910390a35050505050565b6000611e9b83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612336565b9392505050565b600082611eb1575060006107fa565b82820282848281611ebe57fe5b0414611e9b5760405162461bcd60e51b8152600401808060200182810382526021815260200180612b926021913960400191505060405180910390fd5b6000611e9b83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612771565b600082820183811015611e9b576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b3390565b6001600160a01b038316611fe05760405162461bcd60e51b8152600401808060200182810382526024815260200180612c416024913960400191505060405180910390fd5b6001600160a01b0382166120255760405162461bcd60e51b8152600401808060200182810382526022815260200180612b4a6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b031660009081526020819052604090205490565b60025490565b604080518082019091526002815261035360f41b6020820152600090826121105760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5060408051808201909152600280825261068760f31b60208301528304906b033b2e3c9fd0803ce800000082190485111561218c5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5082816b033b2e3c9fd0803ce8000000860201816121a657fe5b04949350505050565b6001600160a01b03821661220a576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b612216600083836115c1565b6002546122238183611f3d565b6002556001600160a01b0383166000908152602081905260409020546122498184611f3d565b6001600160a01b03909416600090815260208190526040902093909355505050565b6000821580612278575081155b15612285575060006107fa565b816b019d971e4fe8401e74000000198161229b57fe5b0483111560405180604001604052806002815260200161068760f31b815250906123065760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b50506b033b2e3c9fd0803ce800000091026b019d971e4fe8401e74000000010490565b6115c1838383600161249a565b600081848411156123885760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b505050900390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526115c19084906127d6565b60008183106123f15781611e9b565b5090919050565b6001600160a01b03821661243d5760405162461bcd60e51b8152600401808060200182810382526021815260200180612bfb6021913960400191505060405180910390fd5b612449826000836115c1565b6002546124568183611e59565b6002556001600160a01b03831660009081526020818152604091829020548251606081019093526022808452909261224992869290612b2890830139839190612336565b836124a3611785565b600c556124ae6115c6565b600b556001600160a01b038116156124f5576124c981610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b836124fe611785565b600c556125096115c6565b600b556001600160a01b038116156125505761252481610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d15e00537f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156125df57600080fd5b505afa1580156125f3573d6000803e3d6000fd5b505050506040513d602081101561260957600080fd5b50519050600061261c826113658a612087565b9050600061262d836113658a612087565b9050612643898961263e8a876120a8565b612994565b8515612713576040805163d5ed393360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301528b811660248301528a81166044830152606482018a90526084820185905260a4820184905291517f00000000000000000000000000000000000000000000000000000000000000009092169163d5ed39339160c48082019260009290919082900301818387803b1580156126fa57600080fd5b505af115801561270e573d6000803e3d6000fd5b505050505b876001600160a01b0316896001600160a01b03167f4beccb90f994c31aced7a23b5611020728a23d8ec5cddd1a3e9d97b96fda86668986604051808381526020018281526020019250505060405180910390a3505050505050505050565b600081836127c05760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5060008385816127cc57fe5b0495945050505050565b6127e8826001600160a01b0316612ac8565b612839576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106128775780518252601f199092019160209182019101612858565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146128d9576040519150601f19603f3d011682016040523d82523d6000602084013e6128de565b606091505b509150915081612935576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b80511561298e5780806020019051602081101561295157600080fd5b505161298e5760405162461bcd60e51b815260040180806020018281038252602a815260200180612c8f602a913960400191505060405180910390fd5b50505050565b6001600160a01b0383166129d95760405162461bcd60e51b8152600401808060200182810382526025815260200180612c1c6025913960400191505060405180910390fd5b6001600160a01b038216612a1e5760405162461bcd60e51b8152600401808060200182810382526023815260200180612b056023913960400191505060405180910390fd5b612a298383836115c1565b6000806000856001600160a01b03166001600160a01b03168152602001908152602001600020549050612a7782604051806060016040528060268152602001612b6c60269139839190612336565b6001600160a01b038086166000908152602081905260408082209390935590851681522054612aa69083611f3d565b6001600160a01b03909316600090815260208190526040902092909255505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590612afc57508115155b94935050505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e6365536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e6365ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef45524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737343616c6c6572206973206e6f742052657761726473446973747269627574696f6e20636f6e74726163745361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122069099a377ef37cb6558ed8ed93dae6b3928f497a9fffff549a7e182944bacb4464736f6c634300060c0033000000000000000000000000375cfb020dcda2d02aa47b4adbe94e924d30a67d0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001b4c6576657220696e7465726573742062656172696e672057425443000000000000000000000000000000000000000000000000000000000000000000000000057857425443000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102685760003560e01c80637b0a47ee11610151578063c72c4d10116100c3578063d505accf11610087578063d505accf14610691578063d7020d0a146106e2578063dd62ed3e1461071e578063df136d651461074c578063ebe2b12b14610754578063f866c3191461075c57610268565b8063c72c4d1014610669578063c8f33c9114610671578063cd3daf9d14610679578063d0fc81d214610681578063d1af0c7d1461068957610268565b8063a457c2d711610115578063a457c2d7146105d3578063a9059cbb146105ff578063ae1673351461062b578063b16a19de14610633578063b1bf962d1461063b578063b9844d8d1461064357610268565b80637b0a47ee146105725780637df5bd3b1461057a57806380faa57d1461059d5780638b876347146105a557806395d89b41146105cb57610268565b8063246132f9116101ea57806339509351116101ae57806339509351146104c05780633d18b912146104ec5780634efecaa5146104f457806370a08231146105205780637535d24614610546578063781603761461056a57610268565b8063246132f91461046557806330adf81f1461048a578063313ce567146104925780633644e515146104b0578063386a9525146104b857610268565b8063156e29f611610231578063156e29f6146103c757806318160ddd146103f95780631c1f78eb146104015780631da24f3e1461040957806323b872dd1461042f57610268565b80628cc2621461026d57806306fdde03146102a55780630700037d14610322578063095ea7b3146103485780630afbcdc914610388575b600080fd5b6102936004803603602081101561028357600080fd5b50356001600160a01b0316610792565b60408051918252519081900360200190f35b6102ad610800565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102e75781810151838201526020016102cf565b50505050905090810190601f1680156103145780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102936004803603602081101561033857600080fd5b50356001600160a01b0316610897565b6103746004803603604081101561035e57600080fd5b506001600160a01b0381351690602001356108a9565b604080519115158252519081900360200190f35b6103ae6004803603602081101561039e57600080fd5b50356001600160a01b03166108c6565b6040805192835260208301919091528051918290030190f35b610374600480360360608110156103dd57600080fd5b506001600160a01b0381351690602081013590604001356108e3565b610293610b2a565b610293610c0f565b6102936004803603602081101561041f57600080fd5b50356001600160a01b0316610c2d565b6103746004803603606081101561044557600080fd5b506001600160a01b03813581169160208101359091169060400135610c38565b6104886004803603604081101561047b57600080fd5b5080359060200135610cf8565b005b610293610ffd565b61049a611021565b6040805160ff9092168252519081900360200190f35b61029361102a565b610293611030565b610374600480360360408110156104d657600080fd5b506001600160a01b038135169060200135611036565b610488611084565b6102936004803603604081101561050a57600080fd5b506001600160a01b0381351690602001356111be565b6102936004803603602081101561053657600080fd5b50356001600160a01b031661129e565b61054e61136b565b604080516001600160a01b039092168252519081900360200190f35b6102ad61138f565b6102936113ac565b6104886004803603604081101561059057600080fd5b50803590602001356113b2565b6102936115c6565b610293600480360360208110156105bb57600080fd5b50356001600160a01b03166115d4565b6102ad6115e6565b610374600480360360408110156105e957600080fd5b506001600160a01b038135169060200135611647565b6103746004803603604081101561061557600080fd5b506001600160a01b0381351690602001356116af565b61054e61170c565b61054e611730565b610293611754565b6102936004803603602081101561065957600080fd5b50356001600160a01b031661175e565b61054e611770565b61029361177f565b610293611785565b6102936117d3565b61054e6117d9565b610488600480360360e08110156106a757600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c001356117e8565b610488600480360360808110156106f857600080fd5b506001600160a01b03813581169160208101359091169060408101359060600135611a2f565b6102936004803603604081101561073457600080fd5b506001600160a01b0381358116916020013516611c7b565b610293611ca6565b610293611cac565b6104886004803603606081101561077257600080fd5b506001600160a01b03813581169160208101359091169060400135611cb2565b6001600160a01b0381166000908152600e6020908152604080832054600d9092528220546107fa91906107f490670de0b6b3a7640000906107ee906107df906107d9611785565b90611e59565b6107e88861129e565b90611ea2565b90611efb565b90611f3d565b92915050565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561088c5780601f106108615761010080835404028352916020019161088c565b820191906000526020600020905b81548152906001019060200180831161086f57829003601f168201915b505050505090505b90565b600e6020526000908152604090205481565b60006108bd6108b6611f97565b8484611f9b565b50600192915050565b6000806108d283612087565b6108da6120a2565b91509150915091565b60007f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b0316610917611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b815250906109c55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561098a578181015183820152602001610972565b50505050905090810190601f1680156109b75780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50836109cf611785565b600c556109da6115c6565b600b556001600160a01b03811615610a21576109f581610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b6000610a2c86612087565b90506000610a3a86866120a8565b6040805180820190915260028152611a9b60f11b602082015290915081610aa25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b50610aad87826121af565b6040805187815290516001600160a01b03891691600091600080516020612bdb8339815191529181900360200190a3604080518781526020810187905281516001600160a01b038a16927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a2501595945050505050565b600080610b356120a2565b905080610b46576000915050610894565b610c097f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b031663d15e00537f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5996040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610bd657600080fd5b505afa158015610bea573d6000803e3d6000fd5b505050506040513d6020811015610c0057600080fd5b5051829061226b565b91505090565b6000610c28600a54600954611ea290919063ffffffff16565b905090565b60006107fa82612087565b6000610c45848484612329565b610cb584610c51611f97565b610cb085604051806060016040528060288152602001612bb3602891396001600160a01b038a16600090815260016020526040812090610c8f611f97565b6001600160a01b031681526020810191909152604001600020549190612336565b611f9b565b826001600160a01b0316846001600160a01b0316600080516020612bdb833981519152846040518082815260200191505060405180910390a35060019392505050565b600f60009054906101000a90046001600160a01b03166001600160a01b031663f2fbb22d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610d4657600080fd5b505afa158015610d5a573d6000803e3d6000fd5b505050506040513d6020811015610d7057600080fd5b50516001600160a01b03163314610db85760405162461bcd60e51b815260040180806020018281038252602a815260200180612c65602a913960400191505060405180910390fd5b6000610dc2611785565b600c55610dcd6115c6565b600b556001600160a01b03811615610e1457610de881610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b600754604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015610e5f57600080fd5b505afa158015610e73573d6000803e3d6000fd5b505050506040513d6020811015610e8957600080fd5b50516008549091504210610f1e57600a839055610ea68484611efb565b600955600a54610eb7908290611efb565b6009541115610f08576040805162461bcd60e51b81526020600482015260186024820152770a0e4deecd2c8cac840e4caeec2e4c840e8dede40d0d2ced60431b604482015290519081900360640190fd5b600a54610f16904290611f3d565b600855610fb8565b600854600090610f2e9042611e59565b90506000610f4760095483611ea290919063ffffffff16565b9050610f57826107ee8884611f3d565b600955610f648383611efb565b6009541115610fb5576040805162461bcd60e51b81526020600482015260186024820152770a0e4deecd2c8cac840e4caeec2e4c840e8dede40d0d2ced60431b604482015290519081900360640190fd5b50505b42600b55604080518581526020810185905281517f6c07ee05dcf262f13abf9d87b846ee789d2f90fe991d495acd7d7fc109ee1f55929181900390910190a150505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60055460ff1690565b60115481565b600a5481565b60006108bd611043611f97565b84610cb08560016000611054611f97565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490611f3d565b600680546001019081905533611098611785565b600c556110a36115c6565b600b556001600160a01b038116156110ea576110be81610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b336000908152600e60205260409020548061110457600080fd5b336000818152600e602052604081205560075461112d916001600160a01b039091169083612390565b60408051828152905133917fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486919081900360200190a2505060065481146111bb576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b50565b60007f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b03166111f2611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b815250906112635760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b506112986001600160a01b037f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599168484612390565b50919050565b60006107fa7f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b031663d15e00537f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5996040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561133057600080fd5b505afa158015611344573d6000803e3d6000fd5b505050506040513d602081101561135a57600080fd5b505161136584612087565b9061226b565b7f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee283981565b604051806040016040528060018152602001603160f81b81525081565b60095481565b7f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b03166113e4611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b815250906114555760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b507f000000000000000000000000a1afe2bf6fa0a9f9aa9f219ce0c087d30aa0760661147f611785565b600c5561148a6115c6565b600b556001600160a01b038116156114d1576114a581610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b826114db576115c1565b61150e7f000000000000000000000000a1afe2bf6fa0a9f9aa9f219ce0c087d30aa0760661150985856120a8565b6121af565b6040805184815290516001600160a01b037f000000000000000000000000a1afe2bf6fa0a9f9aa9f219ce0c087d30aa076061691600091600080516020612bdb8339815191529181900360200190a3604080518481526020810184905281516001600160a01b037f000000000000000000000000a1afe2bf6fa0a9f9aa9f219ce0c087d30aa0760616927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a25b505050565b6000610c28426008546123e2565b600d6020526000908152604090205481565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561088c5780601f106108615761010080835404028352916020019161088c565b60006108bd611654611f97565b84610cb085604051806060016040528060258152602001612cb9602591396001600061167e611f97565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190612336565b60006116c36116bc611f97565b8484612329565b826001600160a01b03166116d5611f97565b6001600160a01b0316600080516020612bdb833981519152846040518082815260200191505060405180910390a350600192915050565b7f000000000000000000000000a1afe2bf6fa0a9f9aa9f219ce0c087d30aa0760681565b7f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59981565b6000610c286120a2565b60106020526000908152604090205481565b600f546001600160a01b031681565b600b5481565b600061178f610b2a565b61179c5750600c54610894565b610c286117ca6117aa610b2a565b6107ee670de0b6b3a76400006107e86009546107e8600b546107d96115c6565b600c5490611f3d565b60001981565b6007546001600160a01b031681565b6001600160a01b038716611833576040805162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22fa7aba722a960991b604482015290519081900360640190fd5b8342111561187d576040805162461bcd60e51b815260206004820152601260248201527124a72b20a624a22fa2ac2824a920aa24a7a760711b604482015290519081900360640190fd5b6001600160a01b0380881660008181526010602090815260408083205460115482517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98186015280840196909652958c166060860152608085018b905260a0850181905260c08086018b90528251808703909101815260e08601835280519084012061190160f01b6101008701526101028601969096526101228086019690965281518086039096018652610142850180835286519684019690962093909552610162840180825283905260ff88166101828501526101a284018790526101c284018690525191926001926101e28083019392601f198301929081900390910190855afa158015611992573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b0316146119f5576040805162461bcd60e51b8152602060048201526011602482015270494e56414c49445f5349474e415455524560781b604482015290519081900360640190fd5b611a00826001611f3d565b6001600160a01b038a16600090815260106020526040902055611a24898989611f9b565b505050505050505050565b7f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b0316611a61611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b81525090611ad25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5083611adc611785565b600c55611ae76115c6565b600b556001600160a01b03811615611b2e57611b0281610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b6000611b3a84846120a8565b60408051808201909152600281526106a760f31b602082015290915081611ba25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b50611bad86826123f8565b6001600160a01b0385163014611bf157611bf16001600160a01b037f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599168686612390565b6040805185815290516000916001600160a01b03891691600080516020612bdb8339815191529181900360200190a3846001600160a01b0316866001600160a01b03167f5d624aa9c148153ab3446c1b154f660ee7701e549fe9b62dab7171b1c80e6fa28686604051808381526020018281526020019250505060405180910390a3505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600c5481565b60085481565b7f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b0316611ce4611f97565b6001600160a01b03161460405180604001604052806002815260200161323960f01b81525090611d555760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5082611d5f611785565b600c55611d6a6115c6565b600b556001600160a01b03811615611db157611d8581610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b82611dba611785565b600c55611dc56115c6565b600b556001600160a01b03811615611e0c57611de081610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b611e19858585600061249a565b836001600160a01b0316856001600160a01b0316600080516020612bdb833981519152856040518082815260200191505060405180910390a35050505050565b6000611e9b83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612336565b9392505050565b600082611eb1575060006107fa565b82820282848281611ebe57fe5b0414611e9b5760405162461bcd60e51b8152600401808060200182810382526021815260200180612b926021913960400191505060405180910390fd5b6000611e9b83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612771565b600082820183811015611e9b576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b3390565b6001600160a01b038316611fe05760405162461bcd60e51b8152600401808060200182810382526024815260200180612c416024913960400191505060405180910390fd5b6001600160a01b0382166120255760405162461bcd60e51b8152600401808060200182810382526022815260200180612b4a6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b031660009081526020819052604090205490565b60025490565b604080518082019091526002815261035360f41b6020820152600090826121105760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5060408051808201909152600280825261068760f31b60208301528304906b033b2e3c9fd0803ce800000082190485111561218c5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5082816b033b2e3c9fd0803ce8000000860201816121a657fe5b04949350505050565b6001600160a01b03821661220a576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b612216600083836115c1565b6002546122238183611f3d565b6002556001600160a01b0383166000908152602081905260409020546122498184611f3d565b6001600160a01b03909416600090815260208190526040902093909355505050565b6000821580612278575081155b15612285575060006107fa565b816b019d971e4fe8401e74000000198161229b57fe5b0483111560405180604001604052806002815260200161068760f31b815250906123065760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b50506b033b2e3c9fd0803ce800000091026b019d971e4fe8401e74000000010490565b6115c1838383600161249a565b600081848411156123885760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b505050900390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526115c19084906127d6565b60008183106123f15781611e9b565b5090919050565b6001600160a01b03821661243d5760405162461bcd60e51b8152600401808060200182810382526021815260200180612bfb6021913960400191505060405180910390fd5b612449826000836115c1565b6002546124568183611e59565b6002556001600160a01b03831660009081526020818152604091829020548251606081019093526022808452909261224992869290612b2890830139839190612336565b836124a3611785565b600c556124ae6115c6565b600b556001600160a01b038116156124f5576124c981610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b836124fe611785565b600c556125096115c6565b600b556001600160a01b038116156125505761252481610792565b6001600160a01b0382166000908152600e6020908152604080832093909355600c54600d909152919020555b60007f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28396001600160a01b031663d15e00537f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5996040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156125df57600080fd5b505afa1580156125f3573d6000803e3d6000fd5b505050506040513d602081101561260957600080fd5b50519050600061261c826113658a612087565b9050600061262d836113658a612087565b9050612643898961263e8a876120a8565b612994565b8515612713576040805163d5ed393360e01b81526001600160a01b037f0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599811660048301528b811660248301528a81166044830152606482018a90526084820185905260a4820184905291517f000000000000000000000000e2a54ebbbbca51f9db1f4aefbd8b1f995eee28399092169163d5ed39339160c48082019260009290919082900301818387803b1580156126fa57600080fd5b505af115801561270e573d6000803e3d6000fd5b505050505b876001600160a01b0316896001600160a01b03167f4beccb90f994c31aced7a23b5611020728a23d8ec5cddd1a3e9d97b96fda86668986604051808381526020018281526020019250505060405180910390a3505050505050505050565b600081836127c05760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561098a578181015183820152602001610972565b5060008385816127cc57fe5b0495945050505050565b6127e8826001600160a01b0316612ac8565b612839576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106128775780518252601f199092019160209182019101612858565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146128d9576040519150601f19603f3d011682016040523d82523d6000602084013e6128de565b606091505b509150915081612935576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b80511561298e5780806020019051602081101561295157600080fd5b505161298e5760405162461bcd60e51b815260040180806020018281038252602a815260200180612c8f602a913960400191505060405180910390fd5b50505050565b6001600160a01b0383166129d95760405162461bcd60e51b8152600401808060200182810382526025815260200180612c1c6025913960400191505060405180910390fd5b6001600160a01b038216612a1e5760405162461bcd60e51b8152600401808060200182810382526023815260200180612b056023913960400191505060405180910390fd5b612a298383836115c1565b6000806000856001600160a01b03166001600160a01b03168152602001908152602001600020549050612a7782604051806060016040528060268152602001612b6c60269139839190612336565b6001600160a01b038086166000908152602081905260408082209390935590851681522054612aa69083611f3d565b6001600160a01b03909316600090815260208190526040902092909255505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590612afc57508115155b94935050505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e6365536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e6365ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef45524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737343616c6c6572206973206e6f742052657761726473446973747269627574696f6e20636f6e74726163745361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122069099a377ef37cb6558ed8ed93dae6b3928f497a9fffff549a7e182944bacb4464736f6c634300060c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000375cfb020dcda2d02aa47b4adbe94e924d30a67d0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001b4c6576657220696e7465726573742062656172696e672057425443000000000000000000000000000000000000000000000000000000000000000000000000057857425443000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _addressesProvider (address): 0x375Cfb020DCDa2d02Aa47B4aDbe94e924d30A67d
Arg [1] : underlyingAssetAddress (address): 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599
Arg [2] : tokenName (string): Lever interest bearing WBTC
Arg [3] : tokenSymbol (string): xWBTC
Arg [4] : decimals (uint8): 8
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000375cfb020dcda2d02aa47b4adbe94e924d30a67d
Arg [1] : 0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [5] : 000000000000000000000000000000000000000000000000000000000000001b
Arg [6] : 4c6576657220696e7465726573742062656172696e6720574254430000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [8] : 7857425443000000000000000000000000000000000000000000000000000000
Deployed Bytecode Sourcemap
2982:15885:62:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;16543:265;;;;;;;;;;;;;;;;-1:-1:-1;16543:265:62;-1:-1:-1;;;;;16543:265:62;;:::i;:::-;;;;;;;;;;;;;;;;949:86:33;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3478:42:62;;;;;;;;;;;;;;;;-1:-1:-1;3478:42:62;-1:-1:-1;;;;;3478:42:62;;:::i;2791:159:33:-;;;;;;;;;;;;;;;;-1:-1:-1;2791:159:33;;-1:-1:-1;;;;;2791:159:33;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;10990:214:62;;;;;;;;;;;;;;;;-1:-1:-1;10990:214:62;-1:-1:-1;;;;;10990:214:62;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;7426:517;;;;;;;;;;;;;;;;-1:-1:-1;7426:517:62;;-1:-1:-1;;;;;7426:517:62;;;;;;;;;;;:::i;11444:425::-;;;:::i;16816:121::-;;;:::i;10560:168::-;;;;;;;;;;;;;;;;-1:-1:-1;10560:168:62;-1:-1:-1;;;;;10560:168:62;;:::i;3292:402:33:-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;3292:402:33;;;;;;;;;;;;;;;;;:::i;17296:1397:62:-;;;;;;;;;;;;;;;;-1:-1:-1;17296:1397:62;;;;;;;:::i;:::-;;3763:170;;;:::i;1245:86:33:-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;4330:31:62;;;:::i;3287:40::-;;;:::i;3948:208:33:-;;;;;;;;;;;;;;;;-1:-1:-1;3948:208:33;;-1:-1:-1;;;;;3948:208:33;;;;;;:::i;16945:287:62:-;;;:::i;12508:258::-;;;;;;;;;;;;;;;;-1:-1:-1;12508:258:62;;-1:-1:-1;;;;;12508:258:62;;;;;;:::i;9937:298::-;;;;;;;;;;;;;;;;-1:-1:-1;9937:298:62;-1:-1:-1;;;;;9937:298:62;;:::i;4113:33::-;;;:::i;:::-;;;;-1:-1:-1;;;;;4113:33:62;;;;;;;;;;;;;;3529:50;;;:::i;3251:29::-;;;:::i;8180:759::-;;;;;;;;;;;;;;;;-1:-1:-1;8180:759:62;;;;;;;:::i;15976:131::-;;;:::i;3414:57::-;;;;;;;;;;;;;;;;-1:-1:-1;3414:57:62;-1:-1:-1;;;;;3414:57:62;;:::i;1094:90:33:-;;;:::i;4420:332::-;;;;;;;;;;;;;;;;-1:-1:-1;4420:332:33;;-1:-1:-1;;;;;4420:332:33;;;;;;:::i;1947:218::-;;;;;;;;;;;;;;;;-1:-1:-1;1947:218:33;;-1:-1:-1;;;;;1947:218:33;;;;;;:::i;4057:49:62:-;;;:::i;4001:::-;;;:::i;12036:171::-;;;:::i;4279:42::-;;;;;;;;;;;;;;;;-1:-1:-1;4279:42:62;-1:-1:-1;;;;;4279:42:62;;:::i;4153:53::-;;;:::i;3334:29::-;;;:::i;16115:420::-;;;:::i;3942:52::-;;;:::i;3180:26::-;;;:::i;13240:1136::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;13240:1136:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;13240:1136:62;;;;;;;;:::i;6347:669::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;6347:669:62;;;;;;;;;;;;;;;;;;;;;;:::i;2439:173:33:-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;2439:173:33;;;;;;;;;;:::i;3370:35:62:-;;;:::i;3213:31::-;;;:::i;9301:407::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;9301:407:62;;;;;;;;;;;;;;;;;:::i;16543:265::-;-1:-1:-1;;;;;16783:16:62;;16597:7;16783:16;;;:7;:16;;;;;;;;;16699:22;:31;;;;;;16637:163;;16783:16;16637:123;;16755:4;;16637:95;;16678:53;;:16;:14;:16::i;:::-;:20;;:53::i;:::-;16637:18;16647:7;16637:9;:18::i;:::-;:40;;:95::i;:::-;:117;;:123::i;:::-;:145;;:163::i;:::-;16617:183;16543:265;-1:-1:-1;;16543:265:62: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;3478:42:62:-;;;;;;;;;;;;;:::o;2791:159:33:-;2874:4;2887:39;2896:12;:10;:12::i;:::-;2910:7;2919:6;2887:8;:39::i;:::-;-1:-1:-1;2940:4:33;2791:159;;;;:::o;10990:214:62:-;11111:7;11120;11153:21;11169:4;11153:15;:21::i;:::-;11176:19;:17;:19::i;:::-;11145:51;;;;10990:214;;;:::o;7426:517::-;7580:4;-1:-1:-1;;;;;5056:4:62;5032:29;:12;:10;:12::i;:::-;5076:36;;;;;;;;;;;;-1:-1:-1;;;5076:36:62;;;;;-1:-1:-1;;;;;5032:29:62;;;;;5010:113;;;;-1:-1:-1;;;5010:113:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7565:4:::1;4488:16;:14;:16::i;:::-;4465:20;:39:::0;4532:26:::1;:24;:26::i;:::-;4515:14;:43:::0;-1:-1:-1;;;;;4573:21:62;::::1;::::0;4569:157:::1;;4630:15;4637:7;4630:6;:15::i;:::-;-1:-1:-1::0;;;;;4611:16:62;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4694:20:::1;::::0;4660:22:::1;:31:::0;;;;;;:54;4569:157:::1;7597:23:::2;7623:21;7639:4;7623:15;:21::i;:::-;7597:47:::0;-1:-1:-1;7657:20:62::2;7680;:6:::0;7694:5;7680:13:::2;:20::i;:::-;7738:29;::::0;;;;::::2;::::0;;;::::2;::::0;;-1:-1:-1;;;7738:29:62::2;::::0;::::2;::::0;7657:43;;-1:-1:-1;7657:43:62;7711:57:::2;;;::::0;-1:-1:-1;;;7711:57:62;;::::2;;::::0;::::2;::::0;;;;;;;;;;;;;;;;;;;;;;::::2;::::0;;;;-1:-1:-1;7711:57:62;;::::2;;::::0;;::::2;::::0;;;::::2;::::0;::::2;;;;;;7779:25;7785:4;7791:12;7779:5;:25::i;:::-;7820:34;::::0;;;;;;;7837:1:::2;-1:-1:-1::0;;;;;;;7820:34:62;::::2;::::0;7837:1;;7820:34;7837:1;;-1:-1:-1;;7837:1:62;-1:-1:-1;;;;;7820:34:62;;;;::::2;::::0;;::::2;7870:25;::::0;;;;;::::2;::::0;::::2;::::0;;;;;-1:-1:-1;;;;;7870:25:62;::::2;::::0;::::2;::::0;;;;;;::::2;-1:-1:-1::0;7915:20:62;;7426:517;-1:-1:-1;;;;;7426:517:62:o;11444:425::-;11560:7;11585:27;11615:19;:17;:19::i;:::-;11585:49;-1:-1:-1;11651:24:62;11647:65;;11699:1;11692:8;;;;;11647:65;11789:57;;;-1:-1:-1;;;11789:57:62;;-1:-1:-1;;;;;11821:24:62;11789:57;;;;;;;;11744:117;;11789:4;:31;;;;:57;;;;;;;;;;;;;;:31;:57;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;11789:57:62;11744:19;;:26;:117::i;:::-;11724:137;;;11444:425;:::o;16816:121::-;16871:7;16898:31;16913:15;;16898:10;;:14;;:31;;;;:::i;:::-;16891:38;;16816:121;:::o;10560:168::-;10667:7;10699:21;10715:4;10699:15;:21::i;3292:402:33:-;3418:4;3431:36;3441:6;3449:9;3460:6;3431:9;:36::i;:::-;3474:149;3491:6;3506:12;:10;:12::i;:::-;3527:89;3565:6;3527:89;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3527:19:33;;;;;;-1:-1:-1;3527:19:33;;;;;;3547:12;:10;:12::i;:::-;-1:-1:-1;;;;;3527:33:33;;;;;;;;;;;;-1:-1:-1;3527:33:33;;;;:37;:89::i;:::-;3474:8;:149::i;:::-;-1:-1:-1;;;;;;;;3635:35:33;;;;;;;;-1:-1:-1;;;;;;;;3635:35:33;;;;;;;;;;;;;;;;-1:-1:-1;3684:4:33;3292:402;;;;;:::o;17296:1397:62:-;4835:17;;:42;;;-1:-1:-1;;;4835:42:62;;;;-1:-1:-1;;;;;4835:17:62;;;;:40;;:42;;;;;;;;;;;;;;;:17;:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4835:42:62;-1:-1:-1;;;;;4821:56:62;:10;:56;4799:148;;;;-1:-1:-1;;;4799:148:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17447:1:::1;4488:16;:14;:16::i;:::-;4465:20;:39:::0;4532:26:::1;:24;:26::i;:::-;4515:14;:43:::0;-1:-1:-1;;;;;4573:21:62;::::1;::::0;4569:157:::1;;4630:15;4637:7;4630:6;:15::i;:::-;-1:-1:-1::0;;;;;4611:16:62;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4694:20:::1;::::0;4660:22:::1;:31:::0;;;;;;:54;4569:157:::1;17833:12:::2;::::0;:37:::2;::::0;;-1:-1:-1;;;17833:37:62;;17864:4:::2;17833:37;::::0;::::2;::::0;;;-1:-1:-1;;;;;;;17833:12:62::2;::::0;-1:-1:-1;;17833:37:62;;;;;::::2;::::0;;;;;;;;:12;:37;::::2;;::::0;::::2;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;::::0;::::2;;-1:-1:-1::0;17833:37:62;17904:12:::2;::::0;17833:37;;-1:-1:-1;17885:15:62::2;:31;17881:707;;17933:15;:34:::0;;;17995:27:::2;:6:::0;17951:16;17995:10:::2;:27::i;:::-;17982:10;:40:::0;18089:15:::2;::::0;18077:28:::2;::::0;:7;;:11:::2;:28::i;:::-;18063:10;;:42;;18037:128;;;::::0;;-1:-1:-1;;;18037:128:62;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;18037:128:62;;;;;;;;;;;;;::::2;;18215:15;::::0;18195:36:::2;::::0;:15:::2;::::0;:19:::2;:36::i;:::-;18180:12;:51:::0;17881:707:::2;;;18284:12;::::0;18264:17:::2;::::0;18284:33:::2;::::0;18301:15:::2;18284:16;:33::i;:::-;18264:53;;18332:16;18351:25;18365:10;;18351:9;:13;;:25;;;;:::i;:::-;18332:44:::0;-1:-1:-1;18404:35:62::2;18429:9:::0;18404:20:::2;:6:::0;18332:44;18404:10:::2;:20::i;:35::-;18391:10;:48:::0;18494:22:::2;:7:::0;18506:9;18494:11:::2;:22::i;:::-;18480:10;;:36;;18454:122;;;::::0;;-1:-1:-1;;;18454:122:62;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;18454:122:62;;;;;;;;;;;;;::::2;;17881:707;;;18617:15;18600:14;:32:::0;18648:37:::2;::::0;;;;;::::2;::::0;::::2;::::0;;;;;::::2;::::0;;;;;;;;;::::2;4736:1;4958::::1;17296:1397:::0;;:::o;3763:170::-;3814:119;3763:170;:::o;1245:86:33:-;1316:9;;;;1245:86;:::o;4330:31:62:-;;;;:::o;3287:40::-;;;;:::o;3948:208:33:-;4036:4;4049:83;4058:12;:10;:12::i;:::-;4072:7;4081:50;4120:10;4081:11;:25;4093:12;:10;:12::i;:::-;-1:-1:-1;;;;;4081:25:33;;;;;;;;;;;;;;;;;-1:-1:-1;4081:25:33;;;:34;;;;;;;;;;;:38;:50::i;16945:287:62:-;2634:13;:18;;2651:1;2634:18;;;;;16999:10:::1;4488:16;:14;:16::i;:::-;4465:20;:39:::0;4532:26:::1;:24;:26::i;:::-;4515:14;:43:::0;-1:-1:-1;;;;;4573:21:62;::::1;::::0;4569:157:::1;;4630:15;4637:7;4630:6;:15::i;:::-;-1:-1:-1::0;;;;;4611:16:62;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4694:20:::1;::::0;4660:22:::1;:31:::0;;;;;;:54;4569:157:::1;17047:10:::2;17022:14;17039:19:::0;;;:7:::2;:19;::::0;;;;;17077:10;17069:19:::2;;;::::0;::::2;;17107:10;17121:1;17099:19:::0;;;:7:::2;:19;::::0;;;;:23;17133:12:::2;::::0;:45:::2;::::0;-1:-1:-1;;;;;17133:12:62;;::::2;::::0;17171:6;17133:25:::2;:45::i;:::-;17194:30;::::0;;;;;;;17205:10:::2;::::0;17194:30:::2;::::0;;;;;::::2;::::0;;::::2;4736:1;2710::::1;2760:13:::0;;2744:12;:29;2722:110;;;;;-1:-1:-1;;;2722:110:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;16945:287;:::o;12508:258::-;12648:7;-1:-1:-1;;;;;5056:4:62;5032:29;:12;:10;:12::i;:::-;5076:36;;;;;;;;;;;;-1:-1:-1;;;5076:36:62;;;;;-1:-1:-1;;;;;5032:29:62;;;;;5010:113;;;;-1:-1:-1;;;5010:113:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5010:113:62;;;;;;;;;;;;;;;;;-1:-1:-1;12673:61:62::1;12680:24;-1:-1:-1::0;;;;;12673:45:62::1;12719:6:::0;12727;12673:45:::1;:61::i;:::-;-1:-1:-1::0;12752:6:62;12508:258;-1:-1:-1;12508:258:62:o;9937:298::-;10063:7;10108:119;10155:4;-1:-1:-1;;;;;10155:31:62;;10187:24;10155:57;;;;;;;;;;;;;-1:-1:-1;;;;;10155:57:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;10155:57:62;10108:21;10124:4;10108:15;:21::i;:::-;:28;;:119::i;4113:33::-;;;:::o;3529:50::-;3569:10;;;;;;;;;;;;-1:-1:-1;;;3569:10:62;;;;3529:50;:::o;3251:29::-;;;;:::o;8180:759::-;-1:-1:-1;;;;;5056:4:62;5032:29;:12;:10;:12::i;:::-;5076:36;;;;;;;;;;;;-1:-1:-1;;;5076:36:62;;;;;-1:-1:-1;;;;;5032:29:62;;;;;5010:113;;;;-1:-1:-1;;;5010:113:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5010:113:62;;;;;;;;;;;;;;;;;;8317:24:::1;4488:16;:14;:16::i;:::-;4465:20;:39:::0;4532:26:::1;:24;:26::i;:::-;4515:14;:43:::0;-1:-1:-1;;;;;4573:21:62;::::1;::::0;4569:157:::1;;4630:15;4637:7;4630:6;:15::i;:::-;-1:-1:-1::0;;;;;4611:16:62;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4694:20:::1;::::0;4660:22:::1;:31:::0;;;;;;:54;4569:157:::1;8363:11:::0;8359:50:::2;;8391:7;;8359:50;8747:53;8753:24;8779:20;:6:::0;8793:5;8779:13:::2;:20::i;:::-;8747:5;:53::i;:::-;8816:54;::::0;;;;;;;8833:1:::2;-1:-1:-1::0;;8837:24:62::2;-1:-1:-1::0;;;;;8816:54:62::2;::::0;8833:1;;8816:54;8833:1;;-1:-1:-1;;8833:1:62;-1:-1:-1;;;;;8816:54:62;;;;::::2;::::0;;::::2;8886:45;::::0;;;;;::::2;::::0;::::2;::::0;;;;;8891:24:::2;-1:-1:-1::0;;;;;8886:45:62::2;::::0;::::2;::::0;;;;;;::::2;4736:1;5134::::1;8180:759:::0;;:::o;15976:131::-;16033:7;16060:39;16069:15;16086:12;;16060:8;:39::i;3414: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;;;;;;;;;;;;;;;;;;;;;;;;4420:332;4528:4;4544:184;4561:12;:10;:12::i;:::-;4582:7;4598:123;4647:15;4598:123;;;;;;;;;;;;;;;;;:11;:25;4610:12;:10;:12::i;:::-;-1:-1:-1;;;;;4598:25:33;;;;;;;;;;;;;;;;;-1:-1:-1;4598:25:33;;;:34;;;;;;;;;;;;:38;:123::i;1947:218::-;2033:4;2046:42;2056:12;:10;:12::i;:::-;2070:9;2081:6;2046:9;:42::i;:::-;-1:-1:-1;;;;;2100:41:33;;2109:12;:10;:12::i;:::-;-1:-1:-1;;;;;2100:41:33;-1:-1:-1;;;;;;;;;;;2100:41:33;;;;;;;;;;;;;;;;-1:-1:-1;2155:4:33;1947:218;;;;:::o;4057:49:62:-;;;:::o;4001:::-;;;:::o;12036:171::-;12148:7;12180:19;:17;:19::i;4279:42::-;;;;;;;;;;;;;:::o;4153:53::-;;;-1:-1:-1;;;;;4153:53:62;;:::o;3334:29::-;;;;:::o;16115:420::-;16162:7;16186:13;:11;:13::i;:::-;16182:78;;-1:-1:-1;16228:20:62;;16221:27;;16182:78;16290:237;16333:179;16498:13;:11;:13::i;:::-;16333:138;16466:4;16333:106;16428:10;;16333:68;16386:14;;16333:26;:24;:26::i;:179::-;16290:20;;;:24;:237::i;3942:52::-;-1:-1:-1;;3942:52:62;:::o;3180:26::-;;;-1:-1:-1;;;;;3180:26:62;;:::o;13240:1136::-;-1:-1:-1;;;;;13450:19:62;;13442:45;;;;;-1:-1:-1;;;13442:45:62;;;;;;;;;;;;-1:-1:-1;;;13442:45:62;;;;;;;;;;;;;;;13561:8;13542:15;:27;;13534:58;;;;;-1:-1:-1;;;13534:58:62;;;;;;;;;;;;-1:-1:-1;;;13534:58:62;;;;;;;;;;;;;;;-1:-1:-1;;;;;13631:14:62;;;13603:25;13631:14;;;:7;:14;;;;;;;;;13786:16;;13861:280;;3814:119;13861:280;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;13861:280:62;;;;;;;;;;;;;;;;;;;;;;;;;;;13825:339;;;;;;-1:-1:-1;;;13714:469:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13686:512;;;;;;;;;14226:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13686:512;;-1:-1:-1;;14226:26:62;;;;;13631:14;-1:-1:-1;;14226:26:62;;;;;;;;;;;-1:-1:-1;14226:26:62;;;;;;;;;;;;;;;-1:-1:-1;;14226:26:62;;-1:-1:-1;;14226:26:62;;-1:-1:-1;;;;;14217:35:62;;;;;;;-1:-1:-1;14209:65:62;;;;;-1:-1:-1;;;14209:65:62;;;;;;;;;;;;-1:-1:-1;;;14209:65:62;;;;;;;;;;;;;;;14302:24;:17;14324:1;14302:21;:24::i;:::-;-1:-1:-1;;;;;14285:14:62;;;;;;:7;:14;;;;;:41;14337:31;14285:14;14353:7;14362:5;14337:8;:31::i;:::-;13240:1136;;;;;;;;;:::o;6347:669::-;-1:-1:-1;;;;;5056:4:62;5032:29;:12;:10;:12::i;:::-;5076:36;;;;;;;;;;;;-1:-1:-1;;;5076:36:62;;;;;-1:-1:-1;;;;;5032:29:62;;;;;5010:113;;;;-1:-1:-1;;;5010:113:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5010:113:62;;;;;;;;;;;;;;;;;;6525:4:::1;4488:16;:14;:16::i;:::-;4465:20;:39:::0;4532:26:::1;:24;:26::i;:::-;4515:14;:43:::0;-1:-1:-1;;;;;4573:21:62;::::1;::::0;4569:157:::1;;4630:15;4637:7;4630:6;:15::i;:::-;-1:-1:-1::0;;;;;4611:16:62;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4694:20:::1;::::0;4660:22:::1;:31:::0;;;;;;:54;4569:157:::1;6542:20:::2;6565;:6:::0;6579:5;6565:13:::2;:20::i;:::-;6623:29;::::0;;;;::::2;::::0;;;::::2;::::0;;-1:-1:-1;;;6623:29:62::2;::::0;::::2;::::0;6542:43;;-1:-1:-1;6542:43:62;6596:57:::2;;;::::0;-1:-1:-1;;;6596:57:62;;::::2;;::::0;::::2;::::0;;;;;;;;;;;;;;;;;;;;;;::::2;::::0;;;;-1:-1:-1;6596:57:62;;::::2;;::::0;;::::2;::::0;;;::::2;::::0;::::2;;;;;;6664:25;6670:4;6676:12;6664:5;:25::i;:::-;6736:4;-1:-1:-1::0;;;;;6704:37:62;::::2;;6700:194;;6758:124;6765:24;-1:-1:-1::0;;;;;6758:45:62::2;6822:20:::0;6861:6;6758:45:::2;:124::i;:::-;6911:34;::::0;;;;;;;6934:1:::2;::::0;-1:-1:-1;;;;;6911:34:62;::::2;::::0;-1:-1:-1;;;;;;;;;;;6911:34:62;;;;::::2;::::0;;::::2;6961:47;::::0;;;;;::::2;::::0;::::2;::::0;;;;;-1:-1:-1;;;;;6961:47:62;;::::2;::::0;;;::::2;::::0;::::2;::::0;;;;;;;;;::::2;4736:1;5134::::1;6347:669:::0;;;;:::o;2439:173:33:-;-1:-1:-1;;;;;2579:18:33;;;2553:7;2579:18;;;-1:-1:-1;2579:18:33;;;;;;;;:27;;;;;;;;;;;;;2439:173::o;3370:35:62:-;;;;:::o;3213:31::-;;;;:::o;9301:407::-;-1:-1:-1;;;;;5056:4:62;5032:29;:12;:10;:12::i;:::-;5076:36;;;;;;;;;;;;-1:-1:-1;;;5076:36:62;;;;;-1:-1:-1;;;;;5032:29:62;;;;;5010:113;;;;-1:-1:-1;;;5010:113:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5010:113:62;;;;;;;;;;;;;;;;;;9453:4:::1;4488:16;:14;:16::i;:::-;4465:20;:39:::0;4532:26:::1;:24;:26::i;:::-;4515:14;:43:::0;-1:-1:-1;;;;;4573:21:62;::::1;::::0;4569:157:::1;;4630:15;4637:7;4630:6;:15::i;:::-;-1:-1:-1::0;;;;;4611:16:62;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4694:20:::1;::::0;4660:22:::1;:31:::0;;;;;;:54;4569:157:::1;9472:2:::2;4488:16;:14;:16::i;:::-;4465:20;:39:::0;4532:26:::2;:24;:26::i;:::-;4515:14;:43:::0;-1:-1:-1;;;;;4573:21:62;::::2;::::0;4569:157:::2;;4630:15;4637:7;4630:6;:15::i;:::-;-1:-1:-1::0;;;;;4611:16:62;::::2;;::::0;;;:7:::2;:16;::::0;;;;;;;:34;;;;4694:20:::2;::::0;4660:22:::2;:31:::0;;;;;;:54;4569:157:::2;9626:33:::3;9636:4;9642:2;9646:5;9653;9626:9;:33::i;:::-;-1:-1:-1::0;;;;;;;;9675:25:62;;::::3;::::0;;;::::3;::::0;-1:-1:-1;;;;;;;;9675:25:62::3;::::0;;;;;;;;;;;::::3;::::0;;::::3;4736:1:::2;5134::::1;9301:407:::0;;;:::o;1299:130:52:-;1357:7;1380:43;1384:1;1387;1380:43;;;;;;;;;;;;;;;;;:3;:43::i;:::-;1373:50;1299:130;-1:-1:-1;;;1299:130:52:o;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;;;;;;;;;;;;;;;;;;;;;;;;;;;601:100:5;685:10;601:100;:::o;6170:348:33:-;-1:-1:-1;;;;;6288:19:33;;6280:68;;;;-1:-1:-1;;;6280:68:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;6363:21:33;;6355:68;;;;-1:-1:-1;;;6355:68:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;6432:18:33;;;;;;;-1:-1:-1;6432:18:33;;;;;;;;:27;;;;;;;;;;;;;:36;;;6480:32;;;;;;;;;;;;;;;;;6170:348;;;:::o;1558:121::-;-1:-1:-1;;;;;1655:18:33;1632:7;1655:18;;;;;;;;;;;;1558:121::o;1396:102::-;1480:12;;1396:102;:::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;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;15803:165:62:-;15927:33;15937:4;15943:2;15947:6;15955:4;15927:9;:33::i;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;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;900:106:62:-;958:7;989:1;985;:5;:13;;997:1;985:13;;;-1:-1:-1;993:1:62;;900:106;-1:-1:-1;900:106:62:o;5715:449:33:-;-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;:21;:67::i;14747:802:62:-;14888:4;4488:16;:14;:16::i;:::-;4465:20;:39;4532:26;:24;:26::i;:::-;4515:14;:43;-1:-1:-1;;;;;4573:21:62;;;4569:157;;4630:15;4637:7;4630:6;:15::i;:::-;-1:-1:-1;;;;;4611:16:62;;;;;;:7;:16;;;;;;;;:34;;;;4694:20;;4660:22;:31;;;;;;:54;4569:157;14907:2:::1;4488:16;:14;:16::i;:::-;4465:20;:39:::0;4532:26:::1;:24;:26::i;:::-;4515:14;:43:::0;-1:-1:-1;;;;;4573:21:62;::::1;::::0;4569:157:::1;;4630:15;4637:7;4630:6;:15::i;:::-;-1:-1:-1::0;;;;;4611:16:62;::::1;;::::0;;;:7:::1;:16;::::0;;;;;;;:34;;;;4694:20:::1;::::0;4660:22:::1;:31:::0;;;;;;:54;4569:157:::1;14951:57:::2;::::0;;-1:-1:-1;;;14951:57:62;;-1:-1:-1;;;;;14983:24:62::2;14951:57:::0;::::2;;::::0;::::2;::::0;;;-1:-1:-1;;14951:4:62::2;:31;::::0;::::2;::::0;:57;;;;;::::2;::::0;;;;;;;;:31;:57;::::2;;::::0;::::2;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;::::0;::::2;;-1:-1:-1::0;14951:57:62;;-1:-1:-1;15021:25:62::2;15049:35;14951:57:::0;15049:21:::2;15065:4:::0;15049:15:::2;:21::i;:35::-;15021:63;;15095:23;15121:33;15148:5;15121:19;15137:2;15121:15;:19::i;:33::-;15095:59:::0;-1:-1:-1;15167:47:62::2;15183:4:::0;15189:2;15193:20:::2;:6:::0;15207:5;15193:13:::2;:20::i;:::-;15167:15;:47::i;:::-;15229:8;15225:259;;;15254:218;::::0;;-1:-1:-1;;;15254:218:62;;-1:-1:-1;;;;;15294:24:62::2;15254:218:::0;::::2;;::::0;::::2;::::0;;;::::2;::::0;;;;;;::::2;::::0;;;;;;;;;;;;;;;;;;;;;;;;:4:::2;:21:::0;;::::2;::::0;-1:-1:-1;;15254:218:62;;;;;-1:-1:-1;;15254:218:62;;;;;;;;-1:-1:-1;15254:21:62;:218;::::2;;::::0;::::2;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;15225:259;15501:40;::::0;;;;;::::2;::::0;::::2;::::0;;;;;-1:-1:-1;;;;;15501:40:62;;::::2;::::0;;;::::2;::::0;::::2;::::0;;;;;;;;;::::2;4736:1;;;::::1;14747:802:::0;;;;;:::o;3598:343:52:-;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;4758:538:33:-;-1:-1:-1;;;;;4880:20:33;;4872:70;;;;-1:-1:-1;;;4872:70:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4957:23:33;;4949:71;;;;-1:-1:-1;;;4949:71:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5029:47;5050:6;5058:9;5069:6;5029:20;:47::i;:::-;-1:-1:-1;;;;;5112:17:33;;5085:24;5112:17;;;;;;;;;;;;;5156:70;;;;;;;;;;;;5112:17;;5156:70;;5177:6;;5156:70;;;;;;:16;;:70;:20;:70::i;:::-;-1:-1:-1;;;;;5136:17:33;;;:9;:17;;;;;;;;;;;:90;;;;5256:20;;;;;;;:32;;5281:6;5256:24;:32::i;:::-;-1:-1:-1;;;;;5233:20:33;;;;:9;:20;;;;;;;;;;:55;;;;-1:-1:-1;;;4758:538:33: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
://69099a377ef37cb6558ed8ed93dae6b3928f497a9fffff549a7e182944bacb44
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $0.002197 | 63.0032 | $0.1384 |
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.