Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
BoostedSavingsVault
Compiler Version
v0.5.16+commit.9c3226ce
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity 0.5.16; 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); } interface IBoostedVaultWithLockup { /** * @dev Stakes a given amount of the StakingToken for the sender * @param _amount Units of StakingToken */ function stake(uint256 _amount) external; /** * @dev Stakes a given amount of the StakingToken for a given beneficiary * @param _beneficiary Staked tokens are credited to this address * @param _amount Units of StakingToken */ function stake(address _beneficiary, uint256 _amount) external; /** * @dev Withdraws stake from pool and claims any unlocked rewards. * Note, this function is costly - the args for _claimRewards * should be determined off chain and then passed to other fn */ function exit() external; /** * @dev Withdraws stake from pool and claims any unlocked rewards. * @param _first Index of the first array element to claim * @param _last Index of the last array element to claim */ function exit(uint256 _first, uint256 _last) external; /** * @dev Withdraws given stake amount from the pool * @param _amount Units of the staked token to withdraw */ function withdraw(uint256 _amount) external; /** * @dev Claims only the tokens that have been immediately unlocked, not including * those that are in the lockers. */ function claimReward() external; /** * @dev Claims all unlocked rewards for sender. * Note, this function is costly - the args for _claimRewards * should be determined off chain and then passed to other fn */ function claimRewards() external; /** * @dev Claims all unlocked rewards for sender. Both immediately unlocked * rewards and also locked rewards past their time lock. * @param _first Index of the first array element to claim * @param _last Index of the last array element to claim */ function claimRewards(uint256 _first, uint256 _last) external; /** * @dev Pokes a given account to reset the boost */ function pokeBoost(address _account) external; /** * @dev Gets the RewardsToken */ function getRewardToken() external view returns (IERC20); /** * @dev Gets the last applicable timestamp for this reward period */ function lastTimeRewardApplicable() external view returns (uint256); /** * @dev Calculates the amount of unclaimed rewards per token since last update, * and sums with stored to give the new cumulative reward per token * @return 'Reward' per staked token */ function rewardPerToken() external view returns (uint256); /** * @dev Returned the units of IMMEDIATELY claimable rewards a user has to receive. Note - this * does NOT include the majority of rewards which will be locked up. * @param _account User address * @return Total reward amount earned */ function earned(address _account) external view returns (uint256); /** * @dev Calculates all unclaimed reward data, finding both immediately unlocked rewards * and those that have passed their time lock. * @param _account User address * @return amount Total units of unclaimed rewards * @return first Index of the first userReward that has unlocked * @return last Index of the last userReward that has unlocked */ function unclaimedRewards(address _account) external view returns ( uint256 amount, uint256 first, uint256 last ); } contract ModuleKeys { // Governance // =========== // keccak256("Governance"); bytes32 internal constant KEY_GOVERNANCE = 0x9409903de1e6fd852dfc61c9dacb48196c48535b60e25abf92acc92dd689078d; //keccak256("Staking"); bytes32 internal constant KEY_STAKING = 0x1df41cd916959d1163dc8f0671a666ea8a3e434c13e40faef527133b5d167034; //keccak256("ProxyAdmin"); bytes32 internal constant KEY_PROXY_ADMIN = 0x96ed0203eb7e975a4cbcaa23951943fa35c5d8288117d50c12b3d48b0fab48d1; // mStable // ======= // keccak256("OracleHub"); bytes32 internal constant KEY_ORACLE_HUB = 0x8ae3a082c61a7379e2280f3356a5131507d9829d222d853bfa7c9fe1200dd040; // keccak256("Manager"); bytes32 internal constant KEY_MANAGER = 0x6d439300980e333f0256d64be2c9f67e86f4493ce25f82498d6db7f4be3d9e6f; //keccak256("Recollateraliser"); bytes32 internal constant KEY_RECOLLATERALISER = 0x39e3ed1fc335ce346a8cbe3e64dd525cf22b37f1e2104a755e761c3c1eb4734f; //keccak256("MetaToken"); bytes32 internal constant KEY_META_TOKEN = 0xea7469b14936af748ee93c53b2fe510b9928edbdccac3963321efca7eb1a57a2; // keccak256("SavingsManager"); bytes32 internal constant KEY_SAVINGS_MANAGER = 0x12fe936c77a1e196473c4314f3bed8eeac1d757b319abb85bdda70df35511bf1; // keccak256("Liquidator"); bytes32 internal constant KEY_LIQUIDATOR = 0x1e9cb14d7560734a61fa5ff9273953e971ff3cd9283c03d8346e3264617933d4; } interface INexus { function governor() external view returns (address); function getModule(bytes32 key) external view returns (address); function proposeModule(bytes32 _key, address _addr) external; function cancelProposedModule(bytes32 _key) external; function acceptProposedModule(bytes32 _key) external; function acceptProposedModules(bytes32[] calldata _keys) external; function requestLockModule(bytes32 _key) external; function cancelLockModule(bytes32 _key) external; function lockModule(bytes32 _key) external; } contract InitializableModule2 is ModuleKeys { INexus public constant nexus = INexus(0xAFcE80b19A8cE13DEc0739a1aaB7A028d6845Eb3); /** * @dev Modifier to allow function calls only from the Governor. */ modifier onlyGovernor() { require(msg.sender == _governor(), "Only governor can execute"); _; } /** * @dev Modifier to allow function calls only from the Governance. * Governance is either Governor address or Governance address. */ modifier onlyGovernance() { require( msg.sender == _governor() || msg.sender == _governance(), "Only governance can execute" ); _; } /** * @dev Modifier to allow function calls only from the ProxyAdmin. */ modifier onlyProxyAdmin() { require(msg.sender == _proxyAdmin(), "Only ProxyAdmin can execute"); _; } /** * @dev Modifier to allow function calls only from the Manager. */ modifier onlyManager() { require(msg.sender == _manager(), "Only manager can execute"); _; } /** * @dev Returns Governor address from the Nexus * @return Address of Governor Contract */ function _governor() internal view returns (address) { return nexus.governor(); } /** * @dev Returns Governance Module address from the Nexus * @return Address of the Governance (Phase 2) */ function _governance() internal view returns (address) { return nexus.getModule(KEY_GOVERNANCE); } /** * @dev Return Staking Module address from the Nexus * @return Address of the Staking Module contract */ function _staking() internal view returns (address) { return nexus.getModule(KEY_STAKING); } /** * @dev Return ProxyAdmin Module address from the Nexus * @return Address of the ProxyAdmin Module contract */ function _proxyAdmin() internal view returns (address) { return nexus.getModule(KEY_PROXY_ADMIN); } /** * @dev Return MetaToken Module address from the Nexus * @return Address of the MetaToken Module contract */ function _metaToken() internal view returns (address) { return nexus.getModule(KEY_META_TOKEN); } /** * @dev Return OracleHub Module address from the Nexus * @return Address of the OracleHub Module contract */ function _oracleHub() internal view returns (address) { return nexus.getModule(KEY_ORACLE_HUB); } /** * @dev Return Manager Module address from the Nexus * @return Address of the Manager Module contract */ function _manager() internal view returns (address) { return nexus.getModule(KEY_MANAGER); } /** * @dev Return SavingsManager Module address from the Nexus * @return Address of the SavingsManager Module contract */ function _savingsManager() internal view returns (address) { return nexus.getModule(KEY_SAVINGS_MANAGER); } /** * @dev Return Recollateraliser Module address from the Nexus * @return Address of the Recollateraliser Module contract (Phase 2) */ function _recollateraliser() internal view returns (address) { return nexus.getModule(KEY_RECOLLATERALISER); } } interface IRewardsDistributionRecipient { function notifyRewardAmount(uint256 reward) external; function getRewardToken() external view returns (IERC20); } contract InitializableRewardsDistributionRecipient is IRewardsDistributionRecipient, InitializableModule2 { // @abstract function notifyRewardAmount(uint256 reward) external; function getRewardToken() external view returns (IERC20); // This address has the ability to distribute the rewards address public rewardsDistributor; /** @dev Recipient is a module, governed by mStable governance */ function _initialize(address _rewardsDistributor) internal { rewardsDistributor = _rewardsDistributor; } /** * @dev Only the rewards distributor can notify about rewards */ modifier onlyRewardsDistributor() { require(msg.sender == rewardsDistributor, "Caller is not reward distributor"); _; } /** * @dev Change the rewardsDistributor - only called by mStable governor * @param _rewardsDistributor Address of the new distributor */ function setRewardsDistribution(address _rewardsDistributor) external onlyGovernor { rewardsDistributor = _rewardsDistributor; } } contract IERC20WithCheckpointing { function balanceOf(address _owner) public view returns (uint256); function balanceOfAt(address _owner, uint256 _blockNumber) public view returns (uint256); function totalSupply() public view returns (uint256); function totalSupplyAt(uint256 _blockNumber) public view returns (uint256); } contract IIncentivisedVotingLockup is IERC20WithCheckpointing { function getLastUserPoint(address _addr) external view returns ( int128 bias, int128 slope, uint256 ts ); function createLock(uint256 _value, uint256 _unlockTime) external; function withdraw() external; function increaseLockAmount(uint256 _value) external; function increaseLockLength(uint256 _unlockTime) external; function eject(address _user) external; function expireContract() external; function claimReward() public; function earned(address _account) public view returns (uint256); } /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see {ERC20Detailed}. */ /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. * * _Available since v2.4.0._ */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * * _Available since v2.4.0._ */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * * _Available since v2.4.0._ */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @dev Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * _Available since v2.4.0._ */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. * * _Available since v2.4.0._ */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-call-value (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).sub( value, "SafeERC20: decreased allowance below zero" ); callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance) ); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length require(address(token).isContract(), "SafeERC20: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } contract InitializableReentrancyGuard { bool private _notEntered; function _initialize() internal { // Storing an initial non-zero value makes deployment a bit more // expensive, but in exchange the refund on every call to nonReentrant // will be lower in amount. Since refunds are capped to a percetange of // the total transaction's gas, it is best to keep them low in cases // like this one, to increase the likelihood of the full refund coming // into effect. _notEntered = true; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_notEntered, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _notEntered = false; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _notEntered = true; } } library StableMath { using SafeMath for uint256; /** * @dev Scaling unit for use in specific calculations, * where 1 * 10**18, or 1e18 represents a unit '1' */ uint256 private constant FULL_SCALE = 1e18; /** * @notice Token Ratios are used when converting between units of bAsset, mAsset and MTA * Reasoning: Takes into account token decimals, and difference in base unit (i.e. grams to Troy oz for gold) * @dev bAsset ratio unit for use in exact calculations, * where (1 bAsset unit * bAsset.ratio) / ratioScale == x mAsset unit */ uint256 private constant RATIO_SCALE = 1e8; /** * @dev Provides an interface to the scaling unit * @return Scaling unit (1e18 or 1 * 10**18) */ function getFullScale() internal pure returns (uint256) { return FULL_SCALE; } /** * @dev Provides an interface to the ratio unit * @return Ratio scale unit (1e8 or 1 * 10**8) */ function getRatioScale() internal pure returns (uint256) { return RATIO_SCALE; } /** * @dev Scales a given integer to the power of the full scale. * @param x Simple uint256 to scale * @return Scaled value a to an exact number */ function scaleInteger(uint256 x) internal pure returns (uint256) { return x.mul(FULL_SCALE); } /*************************************** PRECISE ARITHMETIC ****************************************/ /** * @dev Multiplies two precise units, and then truncates by the full scale * @param x Left hand input to multiplication * @param y Right hand input to multiplication * @return Result after multiplying the two inputs and then dividing by the shared * scale unit */ function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) { return mulTruncateScale(x, y, FULL_SCALE); } /** * @dev Multiplies two precise units, and then truncates by the given scale. For example, * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18 * @param x Left hand input to multiplication * @param y Right hand input to multiplication * @param scale Scale unit * @return Result after multiplying the two inputs and then dividing by the shared * scale unit */ function mulTruncateScale( uint256 x, uint256 y, uint256 scale ) internal pure returns (uint256) { // e.g. assume scale = fullScale // z = 10e18 * 9e17 = 9e36 uint256 z = x.mul(y); // return 9e38 / 1e18 = 9e18 return z.div(scale); } /** * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result * @param x Left hand input to multiplication * @param y Right hand input to multiplication * @return Result after multiplying the two inputs and then dividing by the shared * scale unit, rounded up to the closest base unit. */ function mulTruncateCeil(uint256 x, uint256 y) internal pure returns (uint256) { // e.g. 8e17 * 17268172638 = 138145381104e17 uint256 scaled = x.mul(y); // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17 uint256 ceil = scaled.add(FULL_SCALE.sub(1)); // e.g. 13814538111.399...e18 / 1e18 = 13814538111 return ceil.div(FULL_SCALE); } /** * @dev Precisely divides two units, by first scaling the left hand operand. Useful * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17) * @param x Left hand input to division * @param y Right hand input to division * @return Result after multiplying the left operand by the scale, and * executing the division on the right hand input. */ function divPrecisely(uint256 x, uint256 y) internal pure returns (uint256) { // e.g. 8e18 * 1e18 = 8e36 uint256 z = x.mul(FULL_SCALE); // e.g. 8e36 / 10e18 = 8e17 return z.div(y); } /*************************************** RATIO FUNCS ****************************************/ /** * @dev Multiplies and truncates a token ratio, essentially flooring the result * i.e. How much mAsset is this bAsset worth? * @param x Left hand operand to multiplication (i.e Exact quantity) * @param ratio bAsset ratio * @return Result after multiplying the two inputs and then dividing by the ratio scale */ function mulRatioTruncate(uint256 x, uint256 ratio) internal pure returns (uint256 c) { return mulTruncateScale(x, ratio, RATIO_SCALE); } /** * @dev Multiplies and truncates a token ratio, rounding up the result * i.e. How much mAsset is this bAsset worth? * @param x Left hand input to multiplication (i.e Exact quantity) * @param ratio bAsset ratio * @return Result after multiplying the two inputs and then dividing by the shared * ratio scale, rounded up to the closest base unit. */ function mulRatioTruncateCeil(uint256 x, uint256 ratio) internal pure returns (uint256) { // e.g. How much mAsset should I burn for this bAsset (x)? // 1e18 * 1e8 = 1e26 uint256 scaled = x.mul(ratio); // 1e26 + 9.99e7 = 100..00.999e8 uint256 ceil = scaled.add(RATIO_SCALE.sub(1)); // return 100..00.999e8 / 1e8 = 1e18 return ceil.div(RATIO_SCALE); } /** * @dev Precisely divides two ratioed units, by first scaling the left hand operand * i.e. How much bAsset is this mAsset worth? * @param x Left hand operand in division * @param ratio bAsset ratio * @return Result after multiplying the left operand by the scale, and * executing the division on the right hand input. */ function divRatioPrecisely(uint256 x, uint256 ratio) internal pure returns (uint256 c) { // e.g. 1e14 * 1e8 = 1e22 uint256 y = x.mul(RATIO_SCALE); // return 1e22 / 1e12 = 1e10 return y.div(ratio); } /*************************************** HELPERS ****************************************/ /** * @dev Calculates minimum of two numbers * @param x Left hand input * @param y Right hand input * @return Minimum of the two inputs */ function min(uint256 x, uint256 y) internal pure returns (uint256) { return x > y ? y : x; } /** * @dev Calculated maximum of two numbers * @param x Left hand input * @param y Right hand input * @return Maximum of the two inputs */ function max(uint256 x, uint256 y) internal pure returns (uint256) { return x > y ? x : y; } /** * @dev Clamps a value to an upper bound * @param x Left hand input * @param upperBound Maximum possible value to return * @return Input x clamped to a maximum value, upperBound */ function clamp(uint256 x, uint256 upperBound) internal pure returns (uint256) { return x > upperBound ? upperBound : x; } } library Root { using SafeMath for uint256; /** * @dev Returns the square root of a given number * @param x Input * @return y Square root of Input */ function sqrt(uint256 x) internal pure returns (uint256 y) { if (x == 0) return 0; else { uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x8) { r <<= 1; } r = (r.add(x.div(r))) >> 1; r = (r.add(x.div(r))) >> 1; r = (r.add(x.div(r))) >> 1; r = (r.add(x.div(r))) >> 1; r = (r.add(x.div(r))) >> 1; r = (r.add(x.div(r))) >> 1; r = (r.add(x.div(r))) >> 1; // Seven iterations should be enough uint256 r1 = x.div(r); return uint256(r < r1 ? r : r1); } } } interface IBoostDirector { function getBalance(address _user) external returns (uint256); function setDirection( address _old, address _new, bool _pokeNew ) external; function whitelistVaults(address[] calldata _vaults) external; } contract BoostedTokenWrapper is InitializableReentrancyGuard { using SafeMath for uint256; using StableMath for uint256; using SafeERC20 for IERC20; IERC20 public constant stakingToken = IERC20(0x30647a72Dc82d7Fbb1123EA74716aB8A317Eac19); // mStable MTA Staking contract via the BoostDirectorV2 IBoostDirector public constant boostDirector = IBoostDirector(0xBa05FD2f20AE15B0D3f20DDc6870FeCa6ACd3592); uint256 private _totalBoostedSupply; mapping(address => uint256) private _boostedBalances; mapping(address => uint256) private _rawBalances; // Vars for use in the boost calculations uint256 private constant MIN_DEPOSIT = 1e18; uint256 private constant MAX_VMTA = 600000e18; uint256 private constant MAX_BOOST = 3e18; uint256 private constant MIN_BOOST = 1e18; uint256 private constant FLOOR = 98e16; uint256 public constant boostCoeff = 9; uint256 public constant priceCoeff = 1e17; /** * @dev TokenWrapper constructor **/ function _initialize() internal { InitializableReentrancyGuard._initialize(); } /** * @dev Get the total boosted amount * @return uint256 total supply */ function totalSupply() public view returns (uint256) { return _totalBoostedSupply; } /** * @dev Get the boosted balance of a given account * @param _account User for which to retrieve balance */ function balanceOf(address _account) public view returns (uint256) { return _boostedBalances[_account]; } /** * @dev Get the RAW balance of a given account * @param _account User for which to retrieve balance */ function rawBalanceOf(address _account) public view returns (uint256) { return _rawBalances[_account]; } /** * @dev Read the boost for the given address * @param _account User for which to return the boost * @return boost where 1x == 1e18 */ function getBoost(address _account) public view returns (uint256) { return balanceOf(_account).divPrecisely(rawBalanceOf(_account)); } /** * @dev Deposits a given amount of StakingToken from sender * @param _amount Units of StakingToken */ function _stakeRaw(address _beneficiary, uint256 _amount) internal nonReentrant { _rawBalances[_beneficiary] = _rawBalances[_beneficiary].add(_amount); stakingToken.safeTransferFrom(msg.sender, address(this), _amount); } /** * @dev Withdraws a given stake from sender * @param _amount Units of StakingToken */ function _withdrawRaw(uint256 _amount) internal nonReentrant { _rawBalances[msg.sender] = _rawBalances[msg.sender].sub(_amount); stakingToken.safeTransfer(msg.sender, _amount); } /** * @dev Updates the boost for the given address according to the formula * boost = min(0.5 + c * vMTA_balance / imUSD_locked^(7/8), 1.5) * If rawBalance <= MIN_DEPOSIT, boost is 0 * @param _account User for which to update the boost */ function _setBoost(address _account) internal { uint256 rawBalance = _rawBalances[_account]; uint256 boostedBalance = _boostedBalances[_account]; uint256 boost = MIN_BOOST; // Check whether balance is sufficient // is_boosted is used to minimize gas usage uint256 scaledBalance = (rawBalance * priceCoeff) / 1e18; if (rawBalance >= MIN_DEPOSIT) { uint256 votingWeight = boostDirector.getBalance(_account); boost = _computeBoost(scaledBalance, votingWeight); } uint256 newBoostedBalance = rawBalance.mulTruncate(boost); if (newBoostedBalance != boostedBalance) { _totalBoostedSupply = _totalBoostedSupply.sub(boostedBalance).add(newBoostedBalance); _boostedBalances[_account] = newBoostedBalance; } } /** * @dev Computes the boost for * boost = min(m, max(1, 0.95 + c * min(voting_weight, f) / deposit^(3/4))) * @param _scaledDeposit deposit amount in terms of USD */ function _computeBoost(uint256 _scaledDeposit, uint256 _votingWeight) private view returns (uint256 boost) { if (_votingWeight == 0) return MIN_BOOST; // Compute balance to the power 3/4 uint256 sqrt1 = Root.sqrt(_scaledDeposit * 1e6); uint256 sqrt2 = Root.sqrt(sqrt1); uint256 denominator = sqrt1 * sqrt2; boost = (((StableMath.min(_votingWeight, MAX_VMTA) * boostCoeff) / 10) * 1e18) / denominator; boost = StableMath.min(MAX_BOOST, StableMath.max(MIN_BOOST, FLOOR + boost)); } } contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private initializing; /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { require( initializing || isConstructor() || !initialized, "Contract instance has already been initialized" ); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; initialized = true; } _; if (isTopLevelCall) { initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function isConstructor() private view returns (bool) { // extcodesize checks the size of the code stored in an address, and // address returns the current address. Since the code is still not // deployed when running a constructor, any checks on its code size will // yield zero, making it an effective way to detect if a contract is // under construction or not. address self = address(this); uint256 cs; assembly { cs := extcodesize(self) } return cs == 0; } // Reserved storage space to allow for layout changes in the future. uint256[50] private ______gap; } library SafeCast { /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value < 2**128, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value < 2**64, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value < 2**32, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value < 2**16, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value < 2**8, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } } // Internal // Libs /** * @title BoostedSavingsVault * @author Stability Labs Pty. Ltd. * @notice Accrues rewards second by second, based on a users boosted balance * @dev Forked from rewards/staking/StakingRewards.sol * Changes: * - Lockup implemented in `updateReward` hook (20% unlock immediately, 80% locked for 6 months) * - `updateBoost` hook called after every external action to reset a users boost * - Struct packing of common data * - Searching for and claiming of unlocked rewards */ contract BoostedSavingsVault is IBoostedVaultWithLockup, Initializable, InitializableRewardsDistributionRecipient, BoostedTokenWrapper { using StableMath for uint256; using SafeCast for uint256; event RewardAdded(uint256 reward); event Staked(address indexed user, uint256 amount, address payer); event Withdrawn(address indexed user, uint256 amount); event Poked(address indexed user); event RewardPaid(address indexed user, uint256 reward); IERC20 public constant rewardsToken = IERC20(0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2); uint64 public constant DURATION = 7 days; // Length of token lockup, after rewards are earned uint256 public constant LOCKUP = 26 weeks; // Percentage of earned tokens unlocked immediately uint64 public constant UNLOCK = 2e17; // Timestamp for current period finish uint256 public periodFinish; // RewardRate for the rest of the PERIOD uint256 public rewardRate; // Last time any user took action uint256 public lastUpdateTime; // Ever increasing rewardPerToken rate, based on % of total supply uint256 public rewardPerTokenStored; mapping(address => UserData) public userData; // Locked reward tracking mapping(address => Reward[]) public userRewards; mapping(address => uint64) public userClaim; struct UserData { uint128 rewardPerTokenPaid; uint128 rewards; uint64 lastAction; uint64 rewardCount; } struct Reward { uint64 start; uint64 finish; uint128 rate; } /** * @dev StakingRewards is a TokenWrapper and RewardRecipient * Constants added to bytecode at deployTime to reduce SLOAD cost */ function initialize(address _rewardsDistributor) external initializer { InitializableRewardsDistributionRecipient._initialize(_rewardsDistributor); BoostedTokenWrapper._initialize(); } /** * @dev Updates the reward for a given address, before executing function. * Locks 80% of new rewards up for 6 months, vesting linearly from (time of last action + 6 months) to * (now + 6 months). This allows rewards to be distributed close to how they were accrued, as opposed * to locking up for a flat 6 months from the time of this fn call (allowing more passive accrual). */ modifier updateReward(address _account) { _updateReward(_account); _; } function _updateReward(address _account) internal { uint256 currentTime = block.timestamp; uint64 currentTime64 = SafeCast.toUint64(currentTime); // Setting of global vars (uint256 newRewardPerToken, uint256 lastApplicableTime) = _rewardPerToken(); // If statement protects against loss in initialisation case if (newRewardPerToken > 0) { rewardPerTokenStored = newRewardPerToken; lastUpdateTime = lastApplicableTime; // Setting of personal vars based on new globals if (_account != address(0)) { UserData memory data = userData[_account]; uint256 earned = _earned(_account, data.rewardPerTokenPaid, newRewardPerToken); // If earned == 0, then it must either be the initial stake, or an action in the // same block, since new rewards unlock after each block. if (earned > 0) { uint256 unlocked = earned.mulTruncate(UNLOCK); uint256 locked = earned.sub(unlocked); userRewards[_account].push( Reward({ start: SafeCast.toUint64(LOCKUP.add(data.lastAction)), finish: SafeCast.toUint64(LOCKUP.add(currentTime)), rate: SafeCast.toUint128(locked.div(currentTime.sub(data.lastAction))) }) ); userData[_account] = UserData({ rewardPerTokenPaid: SafeCast.toUint128(newRewardPerToken), rewards: SafeCast.toUint128(unlocked.add(data.rewards)), lastAction: currentTime64, rewardCount: data.rewardCount + 1 }); } else { userData[_account] = UserData({ rewardPerTokenPaid: SafeCast.toUint128(newRewardPerToken), rewards: data.rewards, lastAction: currentTime64, rewardCount: data.rewardCount }); } } } else if (_account != address(0)) { // This should only be hit once, for first staker in initialisation case userData[_account].lastAction = currentTime64; } } /** @dev Updates the boost for a given address, after the rest of the function has executed */ modifier updateBoost(address _account) { _; _setBoost(_account); } /*************************************** ACTIONS - EXTERNAL ****************************************/ /** * @dev Stakes a given amount of the StakingToken for the sender * @param _amount Units of StakingToken */ function stake(uint256 _amount) external updateReward(msg.sender) updateBoost(msg.sender) { _stake(msg.sender, _amount); } /** * @dev Stakes a given amount of the StakingToken for a given beneficiary * @param _beneficiary Staked tokens are credited to this address * @param _amount Units of StakingToken */ function stake(address _beneficiary, uint256 _amount) external updateReward(_beneficiary) updateBoost(_beneficiary) { _stake(_beneficiary, _amount); } /** * @dev Withdraws stake from pool and claims any unlocked rewards. * Note, this function is costly - the args for _claimRewards * should be determined off chain and then passed to other fn */ function exit() external updateReward(msg.sender) updateBoost(msg.sender) { _withdraw(rawBalanceOf(msg.sender)); (uint256 first, uint256 last) = _unclaimedEpochs(msg.sender); _claimRewards(first, last); } /** * @dev Withdraws stake from pool and claims any unlocked rewards. * @param _first Index of the first array element to claim * @param _last Index of the last array element to claim */ function exit(uint256 _first, uint256 _last) external updateReward(msg.sender) updateBoost(msg.sender) { _withdraw(rawBalanceOf(msg.sender)); _claimRewards(_first, _last); } /** * @dev Withdraws given stake amount from the pool * @param _amount Units of the staked token to withdraw */ function withdraw(uint256 _amount) external updateReward(msg.sender) updateBoost(msg.sender) { _withdraw(_amount); } /** * @dev Claims only the tokens that have been immediately unlocked, not including * those that are in the lockers. */ function claimReward() external updateReward(msg.sender) updateBoost(msg.sender) { uint256 unlocked = userData[msg.sender].rewards; userData[msg.sender].rewards = 0; if (unlocked > 0) { rewardsToken.safeTransfer(msg.sender, unlocked); emit RewardPaid(msg.sender, unlocked); } } /** * @dev Claims all unlocked rewards for sender. * Note, this function is costly - the args for _claimRewards * should be determined off chain and then passed to other fn */ function claimRewards() external updateReward(msg.sender) updateBoost(msg.sender) { (uint256 first, uint256 last) = _unclaimedEpochs(msg.sender); _claimRewards(first, last); } /** * @dev Claims all unlocked rewards for sender. Both immediately unlocked * rewards and also locked rewards past their time lock. * @param _first Index of the first array element to claim * @param _last Index of the last array element to claim */ function claimRewards(uint256 _first, uint256 _last) external updateReward(msg.sender) updateBoost(msg.sender) { _claimRewards(_first, _last); } /** * @dev Pokes a given account to reset the boost */ function pokeBoost(address _account) external updateReward(_account) updateBoost(_account) { emit Poked(_account); } /*************************************** ACTIONS - INTERNAL ****************************************/ /** * @dev Claims all unlocked rewards for sender. Both immediately unlocked * rewards and also locked rewards past their time lock. * @param _first Index of the first array element to claim * @param _last Index of the last array element to claim */ function _claimRewards(uint256 _first, uint256 _last) internal { (uint256 unclaimed, uint256 lastTimestamp) = _unclaimedRewards(msg.sender, _first, _last); userClaim[msg.sender] = uint64(lastTimestamp); uint256 unlocked = userData[msg.sender].rewards; userData[msg.sender].rewards = 0; uint256 total = unclaimed.add(unlocked); if (total > 0) { rewardsToken.safeTransfer(msg.sender, total); emit RewardPaid(msg.sender, total); } } /** * @dev Internally stakes an amount by depositing from sender, * and crediting to the specified beneficiary * @param _beneficiary Staked tokens are credited to this address * @param _amount Units of StakingToken */ function _stake(address _beneficiary, uint256 _amount) internal { require(_amount > 0, "Cannot stake 0"); require(_beneficiary != address(0), "Invalid beneficiary address"); _stakeRaw(_beneficiary, _amount); emit Staked(_beneficiary, _amount, msg.sender); } /** * @dev Withdraws raw units from the sender * @param _amount Units of StakingToken */ function _withdraw(uint256 _amount) internal { require(_amount > 0, "Cannot withdraw 0"); _withdrawRaw(_amount); emit Withdrawn(msg.sender, _amount); } /*************************************** GETTERS ****************************************/ /** * @dev Gets the RewardsToken */ function getRewardToken() external view returns (IERC20) { return rewardsToken; } /** * @dev Gets the last applicable timestamp for this reward period */ function lastTimeRewardApplicable() public view returns (uint256) { return StableMath.min(block.timestamp, periodFinish); } /** * @dev Calculates the amount of unclaimed rewards per token since last update, * and sums with stored to give the new cumulative reward per token * @return 'Reward' per staked token */ function rewardPerToken() public view returns (uint256) { (uint256 rewardPerToken_, ) = _rewardPerToken(); return rewardPerToken_; } function _rewardPerToken() internal view returns (uint256 rewardPerToken_, uint256 lastTimeRewardApplicable_) { uint256 lastApplicableTime = lastTimeRewardApplicable(); // + 1 SLOAD uint256 timeDelta = lastApplicableTime.sub(lastUpdateTime); // + 1 SLOAD // If this has been called twice in the same block, shortcircuit to reduce gas if (timeDelta == 0) { return (rewardPerTokenStored, lastApplicableTime); } // new reward units to distribute = rewardRate * timeSinceLastUpdate uint256 rewardUnitsToDistribute = rewardRate.mul(timeDelta); // + 1 SLOAD uint256 supply = totalSupply(); // + 1 SLOAD // If there is no StakingToken liquidity, avoid div(0) // If there is nothing to distribute, short circuit if (supply == 0 || rewardUnitsToDistribute == 0) { return (rewardPerTokenStored, lastApplicableTime); } // new reward units per token = (rewardUnitsToDistribute * 1e18) / totalTokens uint256 unitsToDistributePerToken = rewardUnitsToDistribute.divPrecisely(supply); // return summed rate return (rewardPerTokenStored.add(unitsToDistributePerToken), lastApplicableTime); // + 1 SLOAD } /** * @dev Returned the units of IMMEDIATELY claimable rewards a user has to receive. Note - this * does NOT include the majority of rewards which will be locked up. * @param _account User address * @return Total reward amount earned */ function earned(address _account) public view returns (uint256) { uint256 newEarned = _earned( _account, userData[_account].rewardPerTokenPaid, rewardPerToken() ); uint256 immediatelyUnlocked = newEarned.mulTruncate(UNLOCK); return immediatelyUnlocked.add(userData[_account].rewards); } /** * @dev Calculates all unclaimed reward data, finding both immediately unlocked rewards * and those that have passed their time lock. * @param _account User address * @return amount Total units of unclaimed rewards * @return first Index of the first userReward that has unlocked * @return last Index of the last userReward that has unlocked */ function unclaimedRewards(address _account) external view returns ( uint256 amount, uint256 first, uint256 last ) { (first, last) = _unclaimedEpochs(_account); (uint256 unlocked, ) = _unclaimedRewards(_account, first, last); amount = unlocked.add(earned(_account)); } /** @dev Returns only the most recently earned rewards */ function _earned( address _account, uint256 _userRewardPerTokenPaid, uint256 _currentRewardPerToken ) internal view returns (uint256) { // current rate per token - rate user previously received uint256 userRewardDelta = _currentRewardPerToken.sub(_userRewardPerTokenPaid); // + 1 SLOAD // Short circuit if there is nothing new to distribute if (userRewardDelta == 0) { return 0; } // new reward = staked tokens * difference in rate uint256 userNewReward = balanceOf(_account).mulTruncate(userRewardDelta); // + 1 SLOAD // add to previous rewards return userNewReward; } /** * @dev Gets the first and last indexes of array elements containing unclaimed rewards */ function _unclaimedEpochs(address _account) internal view returns (uint256 first, uint256 last) { uint64 lastClaim = userClaim[_account]; uint256 firstUnclaimed = _findFirstUnclaimed(lastClaim, _account); uint256 lastUnclaimed = _findLastUnclaimed(_account); return (firstUnclaimed, lastUnclaimed); } /** * @dev Sums the cumulative rewards from a valid range */ function _unclaimedRewards( address _account, uint256 _first, uint256 _last ) internal view returns (uint256 amount, uint256 latestTimestamp) { uint256 currentTime = block.timestamp; uint64 lastClaim = userClaim[_account]; // Check for no rewards unlocked uint256 totalLen = userRewards[_account].length; if (_first == 0 && _last == 0) { if (totalLen == 0 || currentTime <= userRewards[_account][0].start) { return (0, currentTime); } } // If there are previous unlocks, check for claims that would leave them untouchable if (_first > 0) { require( lastClaim >= userRewards[_account][_first.sub(1)].finish, "Invalid _first arg: Must claim earlier entries" ); } uint256 count = _last.sub(_first).add(1); for (uint256 i = 0; i < count; i++) { uint256 id = _first.add(i); Reward memory rwd = userRewards[_account][id]; require(currentTime >= rwd.start && lastClaim <= rwd.finish, "Invalid epoch"); uint256 endTime = StableMath.min(rwd.finish, currentTime); uint256 startTime = StableMath.max(rwd.start, lastClaim); uint256 unclaimed = endTime.sub(startTime).mul(rwd.rate); amount = amount.add(unclaimed); } // Calculate last relevant timestamp here to allow users to avoid issue of OOG errors // by claiming rewards in batches. latestTimestamp = StableMath.min(currentTime, userRewards[_account][_last].finish); } /** * @dev Uses binarysearch to find the unclaimed lockups for a given account */ function _findFirstUnclaimed(uint64 _lastClaim, address _account) internal view returns (uint256 first) { uint256 len = userRewards[_account].length; if (len == 0) return 0; // Binary search uint256 min = 0; uint256 max = len - 1; // Will be always enough for 128-bit numbers for (uint256 i = 0; i < 128; i++) { if (min >= max) break; uint256 mid = (min.add(max).add(1)).div(2); if (_lastClaim > userRewards[_account][mid].start) { min = mid; } else { max = mid.sub(1); } } return min; } /** * @dev Uses binarysearch to find the unclaimed lockups for a given account */ function _findLastUnclaimed(address _account) internal view returns (uint256 first) { uint256 len = userRewards[_account].length; if (len == 0) return 0; // Binary search uint256 min = 0; uint256 max = len - 1; // Will be always enough for 128-bit numbers for (uint256 i = 0; i < 128; i++) { if (min >= max) break; uint256 mid = (min.add(max).add(1)).div(2); if (now > userRewards[_account][mid].start) { min = mid; } else { max = mid.sub(1); } } return min; } /*************************************** ADMIN ****************************************/ /** * @dev Notifies the contract that new rewards have been added. * Calculates an updated rewardRate based on the rewards in period. * @param _reward Units of RewardToken that have been added to the pool */ function notifyRewardAmount(uint256 _reward) external onlyRewardsDistributor updateReward(address(0)) { require(_reward < 1e24, "Cannot notify with more than a million units"); uint256 currentTime = block.timestamp; // If previous period over, reset rewardRate if (currentTime >= periodFinish) { rewardRate = _reward.div(DURATION); } // If additional reward to existing period, calc sum else { uint256 remaining = periodFinish.sub(currentTime); uint256 leftover = remaining.mul(rewardRate); rewardRate = _reward.add(leftover).div(DURATION); } lastUpdateTime = currentTime; periodFinish = currentTime.add(DURATION); emit RewardAdded(_reward); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"Poked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","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":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"payer","type":"address"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"constant":true,"inputs":[],"name":"DURATION","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LOCKUP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UNLOCK","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"boostCoeff","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"boostDirector","outputs":[{"internalType":"contract IBoostDirector","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"claimReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_first","type":"uint256"},{"internalType":"uint256","name":"_last","type":"uint256"}],"name":"claimRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_first","type":"uint256"},{"internalType":"uint256","name":"_last","type":"uint256"}],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getBoost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getRewardToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_rewardsDistributor","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nexus","outputs":[{"internalType":"contract INexus","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"periodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"pokeBoost","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"priceCoeff","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"rawBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardsDistributor","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardsToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_rewardsDistributor","type":"address"}],"name":"setRewardsDistribution","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"stakingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"unclaimedRewards","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"first","type":"uint256"},{"internalType":"uint256","name":"last","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userClaim","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userData","outputs":[{"internalType":"uint128","name":"rewardPerTokenPaid","type":"uint128"},{"internalType":"uint128","name":"rewards","type":"uint128"},{"internalType":"uint64","name":"lastAction","type":"uint64"},{"internalType":"uint64","name":"rewardCount","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userRewards","outputs":[{"internalType":"uint64","name":"start","type":"uint64"},{"internalType":"uint64","name":"finish","type":"uint64"},{"internalType":"uint128","name":"rate","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50612856806100206000396000f3fe608060405234801561001057600080fd5b50600436106102315760003560e01c80637b0a47ee11610130578063c4d66de8116100b8578063d1af0c7d1161007c578063d1af0c7d146105ff578063df136d6514610607578063e9fad8ee1461060f578063ebe2b12b14610617578063f62aa9751461061f57610231565b8063c4d66de814610542578063c891091314610568578063c8f33c91146105c9578063cd3daf9d146105d1578063cf7bf6b7146105d957610231565b8063a3f5c1d2116100ff578063a3f5c1d2146104e1578063a694fc3a146104e9578063adc9772e14610506578063b43082ec14610532578063b88a802f1461053a57610231565b80637b0a47ee1461048557806380faa57d1461048d578063845aef4b14610495578063949813b81461049d57610231565b806338d3eb38116101be57806363c2a20a1161018257806363c2a20a146103ca57806367ba3d901461042957806369940d791461044f57806370a082311461045757806372f702f31461047d57610231565b806338d3eb38146103565780633c6b16ab1461035e5780633f2a55401461037b578063523993da1461039f578063594dd432146103a757610231565b8063197621431161020557806319762143146102de5780631be05289146103065780632af9cc411461030e5780632e1a7d4d14610331578063372500ab1461034e57610231565b80628cc262146102365780630a6b433f1461026e57806312064c34146102b057806318160ddd146102d6575b600080fd5b61025c6004803603602081101561024c57600080fd5b50356001600160a01b0316610627565b60408051918252519081900360200190f35b6102946004803603602081101561028457600080fd5b50356001600160a01b03166106bb565b604080516001600160401b039092168252519081900360200190f35b61025c600480360360208110156102c657600080fd5b50356001600160a01b03166106d6565b61025c6106f1565b610304600480360360208110156102f457600080fd5b50356001600160a01b03166106f7565b005b610294610786565b6103046004803603604081101561032457600080fd5b508035906020013561078d565b6103046004803603602081101561034757600080fd5b50356107c2565b6103046107e4565b61025c610818565b6103046004803603602081101561037457600080fd5b5035610824565b6103836109a9565b604080516001600160a01b039092168252519081900360200190f35b6102946109b8565b610304600480360360408110156103bd57600080fd5b50803590602001356109c4565b6103f6600480360360408110156103e057600080fd5b506001600160a01b0381351690602001356109d9565b604080516001600160401b0394851681529290931660208301526001600160801b03168183015290519081900360600190f35b61025c6004803603602081101561043f57600080fd5b50356001600160a01b0316610a28565b610383610a51565b61025c6004803603602081101561046d57600080fd5b50356001600160a01b0316610a69565b610383610a84565b61025c610a9c565b61025c610aa2565b61025c610ab5565b6104c3600480360360208110156104b357600080fd5b50356001600160a01b0316610abc565b60408051938452602084019290925282820152519081900360600190f35b610383610b02565b610304600480360360208110156104ff57600080fd5b5035610b1a565b6103046004803603604081101561051c57600080fd5b506001600160a01b038135169060200135610b2f565b610383610b44565b610304610b5c565b6103046004803603602081101561055857600080fd5b50356001600160a01b0316610bf9565b61058e6004803603602081101561057e57600080fd5b50356001600160a01b0316610cac565b604080516001600160801b0395861681529390941660208401526001600160401b039182168385015216606082015290519081900360800190f35b61025c610cee565b61025c610cf4565b610304600480360360208110156105ef57600080fd5b50356001600160a01b0316610d06565b610383610d4f565b61025c610d67565b610304610d6d565b61025c610d90565b61025c610d96565b6001600160a01b0381166000908152603b6020526040812054819061065e9084906001600160801b0316610659610cf4565b610d9b565b9050600061067a826702c68af0bb14000063ffffffff610de616565b6001600160a01b0385166000908152603b60205260409020549091506106b1908290600160801b90046001600160801b0316610dfb565b925050505b919050565b603d602052600090815260409020546001600160401b031681565b6001600160a01b031660009081526036602052604090205490565b60345490565b6106ff610e55565b6001600160a01b0316336001600160a01b031614610764576040805162461bcd60e51b815260206004820152601960248201527f4f6e6c7920676f7665726e6f722063616e206578656375746500000000000000604482015290519081900360640190fd5b603380546001600160a01b0319166001600160a01b0392909216919091179055565b62093a8081565b3361079781610ed5565b336107a96107a4336106d6565b611394565b6107b3848461141f565b6107bc816114f6565b50505050565b336107cc81610ed5565b336107d683611394565b6107df816114f6565b505050565b336107ee81610ed5565b336000806107fb3361163a565b91509150610809828261141f565b5050610814816114f6565b5050565b67016345785d8a000081565b6033546001600160a01b03163314610883576040805162461bcd60e51b815260206004820181905260248201527f43616c6c6572206973206e6f7420726577617264206469737472696275746f72604482015290519081900360640190fd5b600061088e81610ed5565b69d3c21bcecceda100000082106108d65760405162461bcd60e51b815260040180806020018281038252602c815260200180612702602c913960400191505060405180910390fd5b603754429081106108fc576108f48362093a8063ffffffff61168316565b603855610956565b603754600090610912908363ffffffff6116c516565b9050600061092b6038548361170790919063ffffffff16565b905061095062093a80610944878463ffffffff610dfb16565b9063ffffffff61168316565b60385550505b603981905561096e8162093a8063ffffffff610dfb16565b6037556040805184815290517fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d9181900360200190a1505050565b6033546001600160a01b031681565b6702c68af0bb14000081565b336109ce81610ed5565b336107b3848461141f565b603c60205281600052604060002081815481106109f257fe5b6000918252602090912001546001600160401b038082169350600160401b8204169150600160801b90046001600160801b031683565b6000610a4b610a36836106d6565b610a3f84610a69565b9063ffffffff61176016565b92915050565b73a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd290565b6001600160a01b031660009081526035602052604090205490565b7330647a72dc82d7fbb1123ea74716ab8a317eac1981565b60385481565b6000610ab042603754611795565b905090565b62eff10081565b6000806000610aca8461163a565b90925090506000610adc8584846117aa565b509050610af8610aeb86610627565b829063ffffffff610dfb16565b9350509193909250565b73afce80b19a8ce13dec0739a1aab7a028d6845eb381565b33610b2481610ed5565b336107d63384611ae8565b81610b3981610ed5565b826107b38484611ae8565b73ba05fd2f20ae15b0d3f20ddc6870feca6acd359281565b33610b6681610ed5565b336000818152603b6020526040902080546001600160801b03808216909255600160801b9004168015610bef57610bb873a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2338363ffffffff611bd916565b60408051828152905133917fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486919081900360200190a25b50610814816114f6565b600054610100900460ff1680610c125750610c12611c2b565b80610c20575060005460ff16155b610c5b5760405162461bcd60e51b815260040180806020018281038252602e8152602001806127ca602e913960400191505060405180910390fd5b600054610100900460ff16158015610c86576000805460ff1961ff0019909116610100171660011790555b610c8f82610764565b610c97611c31565b8015610814576000805461ff00191690555050565b603b60205260009081526040902080546001909101546001600160801b0380831692600160801b900416906001600160401b0380821691600160401b90041684565b60395481565b600080610cff611c3b565b5091505090565b80610d1081610ed5565b60405182906001600160a01b038216907fa31b3b303c759fa7ee31d89a1a6fb7eb704d8fe5c87aa4f60f54468ff121bee890600090a26107df816114f6565b73a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd281565b603a5481565b33610d7781610ed5565b33610d846107a4336106d6565b6000806107fb3361163a565b60375481565b600981565b600080610dae838563ffffffff6116c516565b905080610dbf576000915050610ddf565b6000610dda82610dce88610a69565b9063ffffffff610de616565b925050505b9392505050565b6000610ddf8383670de0b6b3a7640000611cf0565b600082820183811015610ddf576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600073afce80b19a8ce13dec0739a1aab7a028d6845eb36001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b158015610ea457600080fd5b505afa158015610eb8573d6000803e3d6000fd5b505050506040513d6020811015610ece57600080fd5b5051905090565b426000610ee182611d1e565b9050600080610eee611c3b565b9092509050811561134857603a82905560398190556001600160a01b0385161561134357610f1a6126ba565b506001600160a01b0385166000908152603b60209081526040808320815160808101835281546001600160801b03808216808452600160801b90920416948201949094526001909101546001600160401b0380821693830193909352600160401b900490911660608201529190610f9390889086610d9b565b90508015611263576000610fb5826702c68af0bb14000063ffffffff610de616565b90506000610fc9838363ffffffff6116c516565b9050603c60008a6001600160a01b03166001600160a01b03168152602001908152602001600020604051806060016040528061102661102188604001516001600160401b031662eff100610dfb90919063ffffffff16565b611d1e565b6001600160401b0316815260200161104a61102162eff1008d63ffffffff610dfb16565b6001600160401b0316815260200161109061108b61107e89604001516001600160401b03168e6116c590919063ffffffff16565b869063ffffffff61168316565b611d66565b6001600160801b0390811690915282546001810184556000938452602093849020835191018054948401516040948501518416600160801b026001600160401b03918216600160401b0267ffffffffffffffff60401b199290941667ffffffffffffffff19909716969096171691909117909116929092179091558051608081019091528061111e88611d66565b6001600160801b0316815260200161114f61108b87602001516001600160801b031686610dfb90919063ffffffff16565b6001600160801b03168152602001886001600160401b0316815260200185606001516001016001600160401b0316815250603b60008b6001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a8154816001600160801b0302191690836001600160801b0316021790555060208201518160000160106101000a8154816001600160801b0302191690836001600160801b0316021790555060408201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160010160086101000a8154816001600160401b0302191690836001600160401b031602179055509050505050611340565b604051806080016040528061127786611d66565b6001600160801b0390811682526020858101518216818401526001600160401b03808a166040808601919091526060808901518316958101959095526001600160a01b038d166000908152603b845281902086518154948801518616600160801b029086166fffffffffffffffffffffffffffffffff1990951694909417909416929092178355908401516001909201805494909301518116600160401b0267ffffffffffffffff60401b199290911667ffffffffffffffff1990941693909317169190911790555b50505b61138d565b6001600160a01b0385161561138d576001600160a01b0385166000908152603b60205260409020600101805467ffffffffffffffff19166001600160401b0385161790555b5050505050565b600081116113dd576040805162461bcd60e51b8152602060048201526011602482015270043616e6e6f74207769746864726177203607c1b604482015290519081900360640190fd5b6113e681611daa565b60408051828152905133917f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5919081900360200190a250565b60008061142d3385856117aa565b336000908152603d60209081526040808320805467ffffffffffffffff19166001600160401b038616179055603b909152812080546001600160801b03808216909255939550919350600160801b909204169061148a8483610dfb565b905080156114ee576114b773a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2338363ffffffff611bd916565b60408051828152905133917fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486919081900360200190a25b505050505050565b6001600160a01b038116600090815260366020908152604080832054603590925290912054670de0b6b3a764000067016345785d8a000083028190048184106115d4576040805163f8b2cb4f60e01b81526001600160a01b0387166004820152905160009173ba05fd2f20ae15b0d3f20ddc6870feca6acd35929163f8b2cb4f9160248082019260209290919082900301818787803b15801561159857600080fd5b505af11580156115ac573d6000803e3d6000fd5b505050506040513d60208110156115c257600080fd5b505190506115d08282611e84565b9250505b60006115e6858463ffffffff610de616565b90508381146114ee5761161481611608866034546116c590919063ffffffff16565b9063ffffffff610dfb16565b6034556001600160a01b0386166000908152603560205260409020819055505050505050565b6001600160a01b0381166000908152603d602052604081205481906001600160401b0316816116698286611f29565b9050600061167686612003565b9194509092505050915091565b6000610ddf83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506120d8565b6000610ddf83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061217a565b60008261171657506000610a4b565b8282028284828161172357fe5b0414610ddf5760405162461bcd60e51b81526004018080602001828103825260218152602001806127836021913960400191505060405180910390fd5b60008061177b84670de0b6b3a764000063ffffffff61170716565b905061178d818463ffffffff61168316565b949350505050565b60008183116117a45782610ddf565b50919050565b6001600160a01b0383166000908152603d6020908152604080832054603c909252822054829142916001600160401b0390911690861580156117ea575085155b156118475780158061183357506001600160a01b0388166000908152603c60205260408120805490919061181a57fe5b6000918252602090912001546001600160401b03168311155b15611847575060009350909150611ae09050565b86156118de576001600160a01b0388166000908152603c6020526040902061187688600163ffffffff6116c516565b8154811061188057fe5b6000918252602090912001546001600160401b03600160401b909104811690831610156118de5760405162461bcd60e51b815260040180806020018281038252602e81526020018061272e602e913960400191505060405180910390fd5b60006118f56001611608898b63ffffffff6116c516565b905060005b81811015611a8d5760006119148a8363ffffffff610dfb16565b905061191e6126e1565b6001600160a01b038c166000908152603c6020526040902080548390811061194257fe5b60009182526020918290206040805160608101825292909101546001600160401b03808216808552600160401b830490911694840194909452600160801b90046001600160801b031690820152915087108015906119b6575080602001516001600160401b0316866001600160401b031611155b6119f7576040805162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840cae0dec6d609b1b604482015290519081900360640190fd5b6000611a1082602001516001600160401b031689611795565b90506000611a3483600001516001600160401b0316896001600160401b03166121d4565b90506000611a6884604001516001600160801b0316611a5c84866116c590919063ffffffff16565b9063ffffffff61170716565b9050611a7a8c8263ffffffff610dfb16565b9b5050600190940193506118fa92505050565b506001600160a01b0389166000908152603c602052604090208054611ad99186918a908110611ab857fe5b600091825260209091200154600160401b90046001600160401b0316611795565b9450505050505b935093915050565b60008111611b2e576040805162461bcd60e51b815260206004820152600e60248201526d043616e6e6f74207374616b6520360941b604482015290519081900360640190fd5b6001600160a01b038216611b89576040805162461bcd60e51b815260206004820152601b60248201527f496e76616c69642062656e656669636961727920616464726573730000000000604482015290519081900360640190fd5b611b9382826121ea565b6040805182815233602082015281516001600160a01b038516927f9f9e4044c5742cca66ca090b21552bac14645e68bad7a92364a9d9ff18111a1c928290030190a25050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526107df9084906122d4565b303b1590565b611c3961248c565b565b6000806000611c48610aa2565b90506000611c61603954836116c590919063ffffffff16565b905080611c755750603a5492509050611cec565b603854600090611c8b908363ffffffff61170716565b90506000611c976106f1565b9050801580611ca4575081155b15611cba57603a54849550955050505050611cec565b6000611ccc838363ffffffff61176016565b603a54909150611ce2908263ffffffff610dfb16565b9650939450505050505b9091565b600080611d03858563ffffffff61170716565b9050611d15818463ffffffff61168316565b95945050505050565b6000600160401b8210611d625760405162461bcd60e51b81526004018080602001828103825260268152602001806127a46026913960400191505060405180910390fd5b5090565b6000600160801b8210611d625760405162461bcd60e51b815260040180806020018281038252602781526020018061275c6027913960400191505060405180910390fd5b603354600160a01b900460ff16611e08576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6033805460ff60a01b1916905533600090815260366020526040902054611e35908263ffffffff6116c516565b33600081815260366020526040902091909155611e6e907330647a72dc82d7fbb1123ea74716ab8a317eac19908363ffffffff611bd916565b506033805460ff60a01b1916600160a01b179055565b600081611e9a5750670de0b6b3a7640000610a4b565b6000611eaa84620f4240026124a1565b90506000611eb7826124a1565b905081810280600a6009611ed588697f0e10af47c1c7000000611795565b0281611edd57fe5b04670de0b6b3a76400000281611eef57fe5b049350611f1f6729a2241af62c0000611f1a670de0b6b3a764000087670d99a8cec7e20000016121d4565b611795565b9695505050505050565b6001600160a01b0381166000908152603c602052604081205480611f51576000915050610a4b565b60006000198201815b6080811015611ff857818310611f6f57611ff8565b6000611f8b60026109446001611608888863ffffffff610dfb16565b6001600160a01b0388166000908152603c6020526040902080549192509082908110611fb357fe5b6000918252602090912001546001600160401b039081169089161115611fdb57809350611fef565b611fec81600163ffffffff6116c516565b92505b50600101611f5a565b509095945050505050565b6001600160a01b0381166000908152603c60205260408120548061202b5760009150506106b6565b60006000198201815b60808110156120ce57818310612049576120ce565b600061206560026109446001611608888863ffffffff610dfb16565b6001600160a01b0388166000908152603c602052604090208054919250908290811061208d57fe5b6000918252602090912001546001600160401b03164211156120b1578093506120c5565b6120c281600163ffffffff6116c516565b92505b50600101612034565b5090949350505050565b600081836121645760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612129578181015183820152602001612111565b50505050905090810190601f1680156121565780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161217057fe5b0495945050505050565b600081848411156121cc5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315612129578181015183820152602001612111565b505050900390565b60008183116121e35781610ddf565b5090919050565b603354600160a01b900460ff16612248576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6033805460ff60a01b191690556001600160a01b03821660009081526036602052604090205461227e908263ffffffff610dfb16565b6001600160a01b0383166000908152603660205260409020556122bd7330647a72dc82d7fbb1123ea74716ab8a317eac1933308463ffffffff61262716565b50506033805460ff60a01b1916600160a01b179055565b6122e6826001600160a01b0316612681565b612337576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106123755780518252601f199092019160209182019101612356565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146123d7576040519150601f19603f3d011682016040523d82523d6000602084013e6123dc565b606091505b509150915081612433576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b8051156107bc5780806020019051602081101561244f57600080fd5b50516107bc5760405162461bcd60e51b815260040180806020018281038252602a8152602001806127f8602a913960400191505060405180910390fd5b6033805460ff60a01b1916600160a01b179055565b6000816124b0575060006106b6565b816001600160801b82106124c95760809190911c9060401b5b600160401b82106124df5760409190911c9060201b5b64010000000082106124f65760209190911c9060101b5b62010000821061250b5760109190911c9060081b5b610100821061251f5760089190911c9060041b5b601082106125325760049190911c9060021b5b6008821061253e5760011b5b6001612560612553868463ffffffff61168316565b839063ffffffff610dfb16565b901c90506001612579612553868463ffffffff61168316565b901c90506001612592612553868463ffffffff61168316565b901c905060016125ab612553868463ffffffff61168316565b901c905060016125c4612553868463ffffffff61168316565b901c905060016125dd612553868463ffffffff61168316565b901c905060016125f6612553868463ffffffff61168316565b901c9050600061260c858363ffffffff61168316565b905080821061261b578061261d565b815b93505050506106b6565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526107bc9085906122d4565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906106b1575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b60408051606081018252600080825260208201819052918101919091529056fe43616e6e6f74206e6f746966792077697468206d6f7265207468616e2061206d696c6c696f6e20756e697473496e76616c6964205f6669727374206172673a204d75737420636c61696d206561726c69657220656e747269657353616665436173743a2076616c756520646f65736e27742066697420696e203132382062697473536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7753616665436173743a2076616c756520646f65736e27742066697420696e2036342062697473436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a65645361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a265627a7a723158207bf161c289b9bcda419176bb7e039e1bc01f9e6850bf60a14ac261bcc077433f64736f6c63430005100032
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102315760003560e01c80637b0a47ee11610130578063c4d66de8116100b8578063d1af0c7d1161007c578063d1af0c7d146105ff578063df136d6514610607578063e9fad8ee1461060f578063ebe2b12b14610617578063f62aa9751461061f57610231565b8063c4d66de814610542578063c891091314610568578063c8f33c91146105c9578063cd3daf9d146105d1578063cf7bf6b7146105d957610231565b8063a3f5c1d2116100ff578063a3f5c1d2146104e1578063a694fc3a146104e9578063adc9772e14610506578063b43082ec14610532578063b88a802f1461053a57610231565b80637b0a47ee1461048557806380faa57d1461048d578063845aef4b14610495578063949813b81461049d57610231565b806338d3eb38116101be57806363c2a20a1161018257806363c2a20a146103ca57806367ba3d901461042957806369940d791461044f57806370a082311461045757806372f702f31461047d57610231565b806338d3eb38146103565780633c6b16ab1461035e5780633f2a55401461037b578063523993da1461039f578063594dd432146103a757610231565b8063197621431161020557806319762143146102de5780631be05289146103065780632af9cc411461030e5780632e1a7d4d14610331578063372500ab1461034e57610231565b80628cc262146102365780630a6b433f1461026e57806312064c34146102b057806318160ddd146102d6575b600080fd5b61025c6004803603602081101561024c57600080fd5b50356001600160a01b0316610627565b60408051918252519081900360200190f35b6102946004803603602081101561028457600080fd5b50356001600160a01b03166106bb565b604080516001600160401b039092168252519081900360200190f35b61025c600480360360208110156102c657600080fd5b50356001600160a01b03166106d6565b61025c6106f1565b610304600480360360208110156102f457600080fd5b50356001600160a01b03166106f7565b005b610294610786565b6103046004803603604081101561032457600080fd5b508035906020013561078d565b6103046004803603602081101561034757600080fd5b50356107c2565b6103046107e4565b61025c610818565b6103046004803603602081101561037457600080fd5b5035610824565b6103836109a9565b604080516001600160a01b039092168252519081900360200190f35b6102946109b8565b610304600480360360408110156103bd57600080fd5b50803590602001356109c4565b6103f6600480360360408110156103e057600080fd5b506001600160a01b0381351690602001356109d9565b604080516001600160401b0394851681529290931660208301526001600160801b03168183015290519081900360600190f35b61025c6004803603602081101561043f57600080fd5b50356001600160a01b0316610a28565b610383610a51565b61025c6004803603602081101561046d57600080fd5b50356001600160a01b0316610a69565b610383610a84565b61025c610a9c565b61025c610aa2565b61025c610ab5565b6104c3600480360360208110156104b357600080fd5b50356001600160a01b0316610abc565b60408051938452602084019290925282820152519081900360600190f35b610383610b02565b610304600480360360208110156104ff57600080fd5b5035610b1a565b6103046004803603604081101561051c57600080fd5b506001600160a01b038135169060200135610b2f565b610383610b44565b610304610b5c565b6103046004803603602081101561055857600080fd5b50356001600160a01b0316610bf9565b61058e6004803603602081101561057e57600080fd5b50356001600160a01b0316610cac565b604080516001600160801b0395861681529390941660208401526001600160401b039182168385015216606082015290519081900360800190f35b61025c610cee565b61025c610cf4565b610304600480360360208110156105ef57600080fd5b50356001600160a01b0316610d06565b610383610d4f565b61025c610d67565b610304610d6d565b61025c610d90565b61025c610d96565b6001600160a01b0381166000908152603b6020526040812054819061065e9084906001600160801b0316610659610cf4565b610d9b565b9050600061067a826702c68af0bb14000063ffffffff610de616565b6001600160a01b0385166000908152603b60205260409020549091506106b1908290600160801b90046001600160801b0316610dfb565b925050505b919050565b603d602052600090815260409020546001600160401b031681565b6001600160a01b031660009081526036602052604090205490565b60345490565b6106ff610e55565b6001600160a01b0316336001600160a01b031614610764576040805162461bcd60e51b815260206004820152601960248201527f4f6e6c7920676f7665726e6f722063616e206578656375746500000000000000604482015290519081900360640190fd5b603380546001600160a01b0319166001600160a01b0392909216919091179055565b62093a8081565b3361079781610ed5565b336107a96107a4336106d6565b611394565b6107b3848461141f565b6107bc816114f6565b50505050565b336107cc81610ed5565b336107d683611394565b6107df816114f6565b505050565b336107ee81610ed5565b336000806107fb3361163a565b91509150610809828261141f565b5050610814816114f6565b5050565b67016345785d8a000081565b6033546001600160a01b03163314610883576040805162461bcd60e51b815260206004820181905260248201527f43616c6c6572206973206e6f7420726577617264206469737472696275746f72604482015290519081900360640190fd5b600061088e81610ed5565b69d3c21bcecceda100000082106108d65760405162461bcd60e51b815260040180806020018281038252602c815260200180612702602c913960400191505060405180910390fd5b603754429081106108fc576108f48362093a8063ffffffff61168316565b603855610956565b603754600090610912908363ffffffff6116c516565b9050600061092b6038548361170790919063ffffffff16565b905061095062093a80610944878463ffffffff610dfb16565b9063ffffffff61168316565b60385550505b603981905561096e8162093a8063ffffffff610dfb16565b6037556040805184815290517fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d9181900360200190a1505050565b6033546001600160a01b031681565b6702c68af0bb14000081565b336109ce81610ed5565b336107b3848461141f565b603c60205281600052604060002081815481106109f257fe5b6000918252602090912001546001600160401b038082169350600160401b8204169150600160801b90046001600160801b031683565b6000610a4b610a36836106d6565b610a3f84610a69565b9063ffffffff61176016565b92915050565b73a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd290565b6001600160a01b031660009081526035602052604090205490565b7330647a72dc82d7fbb1123ea74716ab8a317eac1981565b60385481565b6000610ab042603754611795565b905090565b62eff10081565b6000806000610aca8461163a565b90925090506000610adc8584846117aa565b509050610af8610aeb86610627565b829063ffffffff610dfb16565b9350509193909250565b73afce80b19a8ce13dec0739a1aab7a028d6845eb381565b33610b2481610ed5565b336107d63384611ae8565b81610b3981610ed5565b826107b38484611ae8565b73ba05fd2f20ae15b0d3f20ddc6870feca6acd359281565b33610b6681610ed5565b336000818152603b6020526040902080546001600160801b03808216909255600160801b9004168015610bef57610bb873a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2338363ffffffff611bd916565b60408051828152905133917fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486919081900360200190a25b50610814816114f6565b600054610100900460ff1680610c125750610c12611c2b565b80610c20575060005460ff16155b610c5b5760405162461bcd60e51b815260040180806020018281038252602e8152602001806127ca602e913960400191505060405180910390fd5b600054610100900460ff16158015610c86576000805460ff1961ff0019909116610100171660011790555b610c8f82610764565b610c97611c31565b8015610814576000805461ff00191690555050565b603b60205260009081526040902080546001909101546001600160801b0380831692600160801b900416906001600160401b0380821691600160401b90041684565b60395481565b600080610cff611c3b565b5091505090565b80610d1081610ed5565b60405182906001600160a01b038216907fa31b3b303c759fa7ee31d89a1a6fb7eb704d8fe5c87aa4f60f54468ff121bee890600090a26107df816114f6565b73a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd281565b603a5481565b33610d7781610ed5565b33610d846107a4336106d6565b6000806107fb3361163a565b60375481565b600981565b600080610dae838563ffffffff6116c516565b905080610dbf576000915050610ddf565b6000610dda82610dce88610a69565b9063ffffffff610de616565b925050505b9392505050565b6000610ddf8383670de0b6b3a7640000611cf0565b600082820183811015610ddf576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600073afce80b19a8ce13dec0739a1aab7a028d6845eb36001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b158015610ea457600080fd5b505afa158015610eb8573d6000803e3d6000fd5b505050506040513d6020811015610ece57600080fd5b5051905090565b426000610ee182611d1e565b9050600080610eee611c3b565b9092509050811561134857603a82905560398190556001600160a01b0385161561134357610f1a6126ba565b506001600160a01b0385166000908152603b60209081526040808320815160808101835281546001600160801b03808216808452600160801b90920416948201949094526001909101546001600160401b0380821693830193909352600160401b900490911660608201529190610f9390889086610d9b565b90508015611263576000610fb5826702c68af0bb14000063ffffffff610de616565b90506000610fc9838363ffffffff6116c516565b9050603c60008a6001600160a01b03166001600160a01b03168152602001908152602001600020604051806060016040528061102661102188604001516001600160401b031662eff100610dfb90919063ffffffff16565b611d1e565b6001600160401b0316815260200161104a61102162eff1008d63ffffffff610dfb16565b6001600160401b0316815260200161109061108b61107e89604001516001600160401b03168e6116c590919063ffffffff16565b869063ffffffff61168316565b611d66565b6001600160801b0390811690915282546001810184556000938452602093849020835191018054948401516040948501518416600160801b026001600160401b03918216600160401b0267ffffffffffffffff60401b199290941667ffffffffffffffff19909716969096171691909117909116929092179091558051608081019091528061111e88611d66565b6001600160801b0316815260200161114f61108b87602001516001600160801b031686610dfb90919063ffffffff16565b6001600160801b03168152602001886001600160401b0316815260200185606001516001016001600160401b0316815250603b60008b6001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a8154816001600160801b0302191690836001600160801b0316021790555060208201518160000160106101000a8154816001600160801b0302191690836001600160801b0316021790555060408201518160010160006101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160010160086101000a8154816001600160401b0302191690836001600160401b031602179055509050505050611340565b604051806080016040528061127786611d66565b6001600160801b0390811682526020858101518216818401526001600160401b03808a166040808601919091526060808901518316958101959095526001600160a01b038d166000908152603b845281902086518154948801518616600160801b029086166fffffffffffffffffffffffffffffffff1990951694909417909416929092178355908401516001909201805494909301518116600160401b0267ffffffffffffffff60401b199290911667ffffffffffffffff1990941693909317169190911790555b50505b61138d565b6001600160a01b0385161561138d576001600160a01b0385166000908152603b60205260409020600101805467ffffffffffffffff19166001600160401b0385161790555b5050505050565b600081116113dd576040805162461bcd60e51b8152602060048201526011602482015270043616e6e6f74207769746864726177203607c1b604482015290519081900360640190fd5b6113e681611daa565b60408051828152905133917f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5919081900360200190a250565b60008061142d3385856117aa565b336000908152603d60209081526040808320805467ffffffffffffffff19166001600160401b038616179055603b909152812080546001600160801b03808216909255939550919350600160801b909204169061148a8483610dfb565b905080156114ee576114b773a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2338363ffffffff611bd916565b60408051828152905133917fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e0486919081900360200190a25b505050505050565b6001600160a01b038116600090815260366020908152604080832054603590925290912054670de0b6b3a764000067016345785d8a000083028190048184106115d4576040805163f8b2cb4f60e01b81526001600160a01b0387166004820152905160009173ba05fd2f20ae15b0d3f20ddc6870feca6acd35929163f8b2cb4f9160248082019260209290919082900301818787803b15801561159857600080fd5b505af11580156115ac573d6000803e3d6000fd5b505050506040513d60208110156115c257600080fd5b505190506115d08282611e84565b9250505b60006115e6858463ffffffff610de616565b90508381146114ee5761161481611608866034546116c590919063ffffffff16565b9063ffffffff610dfb16565b6034556001600160a01b0386166000908152603560205260409020819055505050505050565b6001600160a01b0381166000908152603d602052604081205481906001600160401b0316816116698286611f29565b9050600061167686612003565b9194509092505050915091565b6000610ddf83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506120d8565b6000610ddf83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061217a565b60008261171657506000610a4b565b8282028284828161172357fe5b0414610ddf5760405162461bcd60e51b81526004018080602001828103825260218152602001806127836021913960400191505060405180910390fd5b60008061177b84670de0b6b3a764000063ffffffff61170716565b905061178d818463ffffffff61168316565b949350505050565b60008183116117a45782610ddf565b50919050565b6001600160a01b0383166000908152603d6020908152604080832054603c909252822054829142916001600160401b0390911690861580156117ea575085155b156118475780158061183357506001600160a01b0388166000908152603c60205260408120805490919061181a57fe5b6000918252602090912001546001600160401b03168311155b15611847575060009350909150611ae09050565b86156118de576001600160a01b0388166000908152603c6020526040902061187688600163ffffffff6116c516565b8154811061188057fe5b6000918252602090912001546001600160401b03600160401b909104811690831610156118de5760405162461bcd60e51b815260040180806020018281038252602e81526020018061272e602e913960400191505060405180910390fd5b60006118f56001611608898b63ffffffff6116c516565b905060005b81811015611a8d5760006119148a8363ffffffff610dfb16565b905061191e6126e1565b6001600160a01b038c166000908152603c6020526040902080548390811061194257fe5b60009182526020918290206040805160608101825292909101546001600160401b03808216808552600160401b830490911694840194909452600160801b90046001600160801b031690820152915087108015906119b6575080602001516001600160401b0316866001600160401b031611155b6119f7576040805162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840cae0dec6d609b1b604482015290519081900360640190fd5b6000611a1082602001516001600160401b031689611795565b90506000611a3483600001516001600160401b0316896001600160401b03166121d4565b90506000611a6884604001516001600160801b0316611a5c84866116c590919063ffffffff16565b9063ffffffff61170716565b9050611a7a8c8263ffffffff610dfb16565b9b5050600190940193506118fa92505050565b506001600160a01b0389166000908152603c602052604090208054611ad99186918a908110611ab857fe5b600091825260209091200154600160401b90046001600160401b0316611795565b9450505050505b935093915050565b60008111611b2e576040805162461bcd60e51b815260206004820152600e60248201526d043616e6e6f74207374616b6520360941b604482015290519081900360640190fd5b6001600160a01b038216611b89576040805162461bcd60e51b815260206004820152601b60248201527f496e76616c69642062656e656669636961727920616464726573730000000000604482015290519081900360640190fd5b611b9382826121ea565b6040805182815233602082015281516001600160a01b038516927f9f9e4044c5742cca66ca090b21552bac14645e68bad7a92364a9d9ff18111a1c928290030190a25050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526107df9084906122d4565b303b1590565b611c3961248c565b565b6000806000611c48610aa2565b90506000611c61603954836116c590919063ffffffff16565b905080611c755750603a5492509050611cec565b603854600090611c8b908363ffffffff61170716565b90506000611c976106f1565b9050801580611ca4575081155b15611cba57603a54849550955050505050611cec565b6000611ccc838363ffffffff61176016565b603a54909150611ce2908263ffffffff610dfb16565b9650939450505050505b9091565b600080611d03858563ffffffff61170716565b9050611d15818463ffffffff61168316565b95945050505050565b6000600160401b8210611d625760405162461bcd60e51b81526004018080602001828103825260268152602001806127a46026913960400191505060405180910390fd5b5090565b6000600160801b8210611d625760405162461bcd60e51b815260040180806020018281038252602781526020018061275c6027913960400191505060405180910390fd5b603354600160a01b900460ff16611e08576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6033805460ff60a01b1916905533600090815260366020526040902054611e35908263ffffffff6116c516565b33600081815260366020526040902091909155611e6e907330647a72dc82d7fbb1123ea74716ab8a317eac19908363ffffffff611bd916565b506033805460ff60a01b1916600160a01b179055565b600081611e9a5750670de0b6b3a7640000610a4b565b6000611eaa84620f4240026124a1565b90506000611eb7826124a1565b905081810280600a6009611ed588697f0e10af47c1c7000000611795565b0281611edd57fe5b04670de0b6b3a76400000281611eef57fe5b049350611f1f6729a2241af62c0000611f1a670de0b6b3a764000087670d99a8cec7e20000016121d4565b611795565b9695505050505050565b6001600160a01b0381166000908152603c602052604081205480611f51576000915050610a4b565b60006000198201815b6080811015611ff857818310611f6f57611ff8565b6000611f8b60026109446001611608888863ffffffff610dfb16565b6001600160a01b0388166000908152603c6020526040902080549192509082908110611fb357fe5b6000918252602090912001546001600160401b039081169089161115611fdb57809350611fef565b611fec81600163ffffffff6116c516565b92505b50600101611f5a565b509095945050505050565b6001600160a01b0381166000908152603c60205260408120548061202b5760009150506106b6565b60006000198201815b60808110156120ce57818310612049576120ce565b600061206560026109446001611608888863ffffffff610dfb16565b6001600160a01b0388166000908152603c602052604090208054919250908290811061208d57fe5b6000918252602090912001546001600160401b03164211156120b1578093506120c5565b6120c281600163ffffffff6116c516565b92505b50600101612034565b5090949350505050565b600081836121645760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612129578181015183820152602001612111565b50505050905090810190601f1680156121565780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161217057fe5b0495945050505050565b600081848411156121cc5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315612129578181015183820152602001612111565b505050900390565b60008183116121e35781610ddf565b5090919050565b603354600160a01b900460ff16612248576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6033805460ff60a01b191690556001600160a01b03821660009081526036602052604090205461227e908263ffffffff610dfb16565b6001600160a01b0383166000908152603660205260409020556122bd7330647a72dc82d7fbb1123ea74716ab8a317eac1933308463ffffffff61262716565b50506033805460ff60a01b1916600160a01b179055565b6122e6826001600160a01b0316612681565b612337576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106123755780518252601f199092019160209182019101612356565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146123d7576040519150601f19603f3d011682016040523d82523d6000602084013e6123dc565b606091505b509150915081612433576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b8051156107bc5780806020019051602081101561244f57600080fd5b50516107bc5760405162461bcd60e51b815260040180806020018281038252602a8152602001806127f8602a913960400191505060405180910390fd5b6033805460ff60a01b1916600160a01b179055565b6000816124b0575060006106b6565b816001600160801b82106124c95760809190911c9060401b5b600160401b82106124df5760409190911c9060201b5b64010000000082106124f65760209190911c9060101b5b62010000821061250b5760109190911c9060081b5b610100821061251f5760089190911c9060041b5b601082106125325760049190911c9060021b5b6008821061253e5760011b5b6001612560612553868463ffffffff61168316565b839063ffffffff610dfb16565b901c90506001612579612553868463ffffffff61168316565b901c90506001612592612553868463ffffffff61168316565b901c905060016125ab612553868463ffffffff61168316565b901c905060016125c4612553868463ffffffff61168316565b901c905060016125dd612553868463ffffffff61168316565b901c905060016125f6612553868463ffffffff61168316565b901c9050600061260c858363ffffffff61168316565b905080821061261b578061261d565b815b93505050506106b6565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526107bc9085906122d4565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906106b1575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b60408051606081018252600080825260208201819052918101919091529056fe43616e6e6f74206e6f746966792077697468206d6f7265207468616e2061206d696c6c696f6e20756e697473496e76616c6964205f6669727374206172673a204d75737420636c61696d206561726c69657220656e747269657353616665436173743a2076616c756520646f65736e27742066697420696e203132382062697473536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7753616665436173743a2076616c756520646f65736e27742066697420696e2036342062697473436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a65645361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a265627a7a723158207bf161c289b9bcda419176bb7e039e1bc01f9e6850bf60a14ac261bcc077433f64736f6c63430005100032
Loading...
Loading
Loading...
Loading
OVERVIEW
Logic contract for mStable Interest Bearing USD (mUSD) Savings Vault.Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
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.