Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 5 from a total of 5 transactions
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
ILVPool
Compiler Version
v0.8.4+commit.c7e474f2
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import { SafeCast } from "./libraries/SafeCast.sol";
import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol";
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import { V2Migrator } from "./base/V2Migrator.sol";
import { CorePool } from "./base/CorePool.sol";
import { ErrorHandler } from "./libraries/ErrorHandler.sol";
import { Stake } from "./libraries/Stake.sol";
import { IFactory } from "./interfaces/IFactory.sol";
import { ICorePool } from "./interfaces/ICorePool.sol";
import { ICorePoolV1 } from "./interfaces/ICorePoolV1.sol";
import { SushiLPPool } from "./SushiLPPool.sol";
/**
* @title ILV Pool
*
* @dev ILV Pool contract to be deployed, with all base contracts inherited.
* @dev Extends functionality working as a router to SushiLP Pool and deployed flash pools.
* through functions like `claimYieldRewardsMultiple()` and `claimVaultRewardsMultiple()`,
* ILV Pool is trusted by other pools and verified by the factory to aggregate functions
* and add quality of life features for stakers.
*/
contract ILVPool is Initializable, V2Migrator {
using ErrorHandler for bytes4;
using Stake for uint256;
using SafeERC20Upgradeable for IERC20Upgradeable;
using SafeCast for uint256;
using BitMaps for BitMaps.BitMap;
/// @dev stores merkle root related to users yield weight in v1.
bytes32 public merkleRoot;
/// @dev bitmap mapping merkle tree user indexes to a bit that tells
/// whether a user has already migrated yield or not.
BitMaps.BitMap internal _usersMigrated;
/// @dev maps `keccak256(userAddress,stakeId)` to a bool value that tells
/// if a v1 yield has already been minted by v2 contract.
mapping(address => mapping(uint256 => bool)) public v1YieldMinted;
/// @dev Used to calculate vault (revenue distribution) rewards, keeps track
/// of the correct ILV balance in the v1 core pool.
uint256 public v1PoolTokenReserve;
/**
* @dev logs `_migratePendingRewards()`
*
* @param from user address
* @param pendingRewardsMigrated value of pending rewards migrated
* @param useSILV whether user claimed v1 pending rewards as ILV or sILV
*/
event LogMigratePendingRewards(address indexed from, uint256 pendingRewardsMigrated, bool useSILV);
/**
* @dev logs `_migrateYieldWeights()`
*
* @param from user address
* @param yieldWeightMigrated total amount of weight coming from yield in v1
*
*/
event LogMigrateYieldWeight(address indexed from, uint256 yieldWeightMigrated);
/**
* @dev logs `mintV1YieldMultiple()`.
*
* @param from user address
* @param value number of ILV tokens minted
*
*/
event LogV1YieldMintedMultiple(address indexed from, uint256 value);
/// @dev Calls `__V2Migrator_init()`.
function initialize(
address ilv_,
address silv_,
address _poolToken,
address factory_,
uint64 _initTime,
uint32 _weight,
address _corePoolV1,
uint256 v1StakeMaxPeriod_
) external initializer {
// calls internal v2 migrator initializer
__V2Migrator_init(ilv_, silv_, _poolToken, _corePoolV1, factory_, _initTime, _weight, v1StakeMaxPeriod_);
}
/**
* @dev Updates value that keeps track of v1 global locked tokens weight.
*
* @param _v1PoolTokenReserve new value to be stored
*/
function setV1PoolTokenReserve(uint256 _v1PoolTokenReserve) external virtual {
// only factory controller can update the _v1GlobalWeight
_requireIsFactoryController();
// update v1PoolTokenReserve state variable
v1PoolTokenReserve = _v1PoolTokenReserve;
}
/// @inheritdoc CorePool
function getTotalReserves() external view virtual override returns (uint256 totalReserves) {
totalReserves = poolTokenReserve + v1PoolTokenReserve;
}
/**
* @dev Sets the yield weight tree root.
* @notice Can only be called by the eDAO.
*
* @param _merkleRoot 32 bytes tree root.
*/
function setMerkleRoot(bytes32 _merkleRoot) external virtual {
// checks if function is being called by PoolFactory.owner()
_requireIsFactoryController();
// stores the merkle root
merkleRoot = _merkleRoot;
}
/**
* @dev Returns whether an user of a given _index in the bitmap has already
* migrated v1 yield weight stored in the merkle tree or not.
*
* @param _index user index in the bitmap, can be checked in the off-chain
* merkle tree
* @return whether user has already migrated yield weights or not
*/
function hasMigratedYield(uint256 _index) public view returns (bool) {
// checks if the merkle tree index linked to a user address has a bit of
// value 0 or 1
return _usersMigrated.get(_index);
}
/**
* @dev Executed by other core pools and flash pools
* as part of yield rewards processing logic (`_claimYieldRewards()` function).
* @dev Executed when _useSILV is false and pool is not an ILV pool -
* see `CorePool._processRewards()`.
*
* @param _staker an address which stakes (the yield reward)
* @param _value amount to be staked (yield reward amount)
*/
function stakeAsPool(address _staker, uint256 _value) external nonReentrant {
// checks if contract is paused
_requireNotPaused();
// expects caller to be a valid contract registered by the pool factory
this.stakeAsPool.selector.verifyAccess(_factory.poolExists(msg.sender));
// gets storage pointer to user
User storage user = users[_staker];
// uses v1 weight values for rewards calculations
uint256 v1WeightToAdd = _useV1Weight(_staker);
// update user state
_updateReward(_staker, v1WeightToAdd);
// calculates take weight based on how much yield has been generated
// (by checking _value) and multiplies by the 2e6 constant, since
// yield is always locked for a year.
uint256 stakeWeight = _value * Stake.YIELD_STAKE_WEIGHT_MULTIPLIER;
// initialize new yield stake being created in memory
Stake.Data memory newStake = Stake.Data({
value: (_value).toUint120(),
lockedFrom: (_now256()).toUint64(),
lockedUntil: (_now256() + Stake.MAX_STAKE_PERIOD).toUint64(),
isYield: true
});
// sum new yield stake weight to user's total weight
user.totalWeight += (stakeWeight).toUint248();
// add the new yield stake to storage
user.stakes.push(newStake);
// update global weight and global pool token count
globalWeight += stakeWeight;
poolTokenReserve += _value;
// emits an event
emit LogStake(
msg.sender,
_staker,
(user.stakes.length - 1),
_value,
(_now256() + Stake.MAX_STAKE_PERIOD).toUint64()
);
}
/**
* @dev Calls internal `_migrateLockedStakes`, `_migrateYieldWeights`
* and `_migratePendingRewards` functions for a complete migration
* of a v1 user to v2.
* @dev See `_migrateLockedStakes` and _`migrateYieldWeights`.
*/
function executeMigration(
bytes32[] calldata _proof,
uint256 _index,
uint248 _yieldWeight,
uint256 _pendingV1Rewards,
bool _useSILV,
uint256[] calldata _stakeIds
) external virtual {
// verifies that user isn't a v1 blacklisted user
_requireNotBlacklisted(msg.sender);
// checks if contract is paused
_requireNotPaused();
// uses v1 weight values for rewards calculations
uint256 v1WeightToAdd = _useV1Weight(msg.sender);
// update user state
_updateReward(msg.sender, v1WeightToAdd);
// call internal migrate locked stake function
// which does the loop to store each v1 stake
// reference in v2 and all required data
_migrateLockedStakes(_stakeIds);
// checks if user is also migrating the v1 yield accumulated weight
if (_yieldWeight > 0) {
// if that's the case, passes the merkle proof with the user index
// in the merkle tree, and the yield weight being migrated
// which will be verified, and then update user state values by the
// internal function
_migrateYieldWeights(_proof, _index, _yieldWeight, _pendingV1Rewards, _useSILV);
}
}
/**
* @dev Calls multiple pools claimYieldRewardsFromRouter() in order to claim yield
* in 1 transaction.
*
* @notice ILV pool works as a router for claiming multiple pools registered
* in the factory.
*
* @param _pools array of pool addresses
* @param _useSILV array of bool values telling if the pool should claim reward
* as ILV or sILV
*/
function claimYieldRewardsMultiple(address[] calldata _pools, bool[] calldata _useSILV) external virtual {
// checks if contract is paused
_requireNotPaused();
// we're using selector to simplify input and access validation
bytes4 fnSelector = this.claimYieldRewardsMultiple.selector;
// checks if user passed the correct number of inputs
fnSelector.verifyInput(_pools.length == _useSILV.length, 0);
// loops over each pool passed to execute the necessary checks, and call
// the functions according to the pool
for (uint256 i = 0; i < _pools.length; i++) {
// gets current pool in the loop
address pool = _pools[i];
// verifies that the given pool is a valid pool registered by the pool
// factory contract
fnSelector.verifyAccess(IFactory(_factory).poolExists(pool));
// if the pool passed is the ILV pool (i.e this contract),
// just calls an internal function
if (ICorePool(pool).poolToken() == _ilv) {
// call internal _claimYieldRewards
_claimYieldRewards(msg.sender, _useSILV[i]);
} else {
// if not, executes a calls to the other core pool which will handle
// the other pool reward claim
SushiLPPool(pool).claimYieldRewardsFromRouter(msg.sender, _useSILV[i]);
}
}
}
/**
* @dev Calls multiple pools claimVaultRewardsFromRouter() in order to claim yield
* in 1 transaction.
*
* @notice ILV pool works as a router for claiming multiple pools registered
* in the factory
*
* @param _pools array of pool addresses
*/
function claimVaultRewardsMultiple(address[] calldata _pools) external virtual {
// checks if contract is paused
_requireNotPaused();
// loops over each pool passed to execute the necessary checks, and call
// the functions according to the pool
for (uint256 i = 0; i < _pools.length; i++) {
// gets current pool in the loop
address pool = _pools[i];
// we're using selector to simplify input and state validation
// checks if the given pool is a valid one registred by the pool
// factory contract
this.claimVaultRewardsMultiple.selector.verifyAccess(IFactory(_factory).poolExists(pool));
// if the pool passed is the ILV pool (i.e this contract),
// just calls an internal function
if (ICorePool(pool).poolToken() == _ilv) {
// call internal _claimVaultRewards
_claimVaultRewards(msg.sender);
} else {
// if not, executes a calls to the other core pool which will handle
// the other pool reward claim
SushiLPPool(pool).claimVaultRewardsFromRouter(msg.sender);
}
}
}
/**
* @dev Aggregates in one single mint call multiple yield stakeIds from v1.
* @dev reads v1 ILV pool to execute checks, if everything is correct, it stores
* in memory total amount of yield to be minted and calls the PoolFactory to mint
* it to msg.sender.
*
* @notice V1 won't be able to mint ILV yield anymore. This mean only this function
* in the V2 contract is able to mint previously accumulated V1 yield.
*
* @param _stakeIds array of yield ids in v1 from msg.sender user
*/
function mintV1YieldMultiple(uint256[] calldata _stakeIds) external virtual {
// we're using function selector to simplify validation
bytes4 fnSelector = this.mintV1YieldMultiple.selector;
// verifies that user isn't a v1 blacklisted user
_requireNotBlacklisted(msg.sender);
// checks if contract is paused
_requireNotPaused();
// gets storage pointer to the user
User storage user = users[msg.sender];
// initialize variables that will be used inside the loop
// to store how much yield needs to be minted and how much
// weight needs to be removed from the user
uint256 amountToMint;
uint256 weightToRemove;
// initializes variable that will store how much v1 weight the user has
uint256 v1WeightToAdd;
// avoids stack too deep error
{
// uses v1 weight values for rewards calculations
uint256 _v1WeightToAdd = _useV1Weight(msg.sender);
// update user state
_updateReward(msg.sender, _v1WeightToAdd);
v1WeightToAdd = _v1WeightToAdd;
}
// loops over each stake id, doing the necessary checks and
// updating the mapping that keep tracks of v1 yield mints.
for (uint256 i = 0; i < _stakeIds.length; i++) {
// gets current stake id in the loop
uint256 _stakeId = _stakeIds[i];
// call v1 core pool to get all required data associated with
// the passed v1 stake id
(uint256 tokenAmount, uint256 _weight, uint64 lockedFrom, uint64 lockedUntil, bool isYield) = ICorePoolV1(
corePoolV1
).getDeposit(msg.sender, _stakeId);
// checks if the obtained v1 stake (through getDeposit)
// is indeed yield
fnSelector.verifyState(isYield, i * 3);
// expects the yield v1 stake to be unlocked
fnSelector.verifyState(_now256() > lockedUntil, i * 4 + 1);
// expects that the v1 stake hasn't been minted yet
fnSelector.verifyState(!v1YieldMinted[msg.sender][_stakeId], i * 5 + 2);
// verifies if the yield has been created before v2 launches
fnSelector.verifyState(lockedFrom < _v1StakeMaxPeriod, i * 6 + 3);
// marks v1 yield as minted
v1YieldMinted[msg.sender][_stakeId] = true;
// updates variables that will be used for minting yield and updating
// user struct later
amountToMint += tokenAmount;
weightToRemove += _weight;
}
// subtracts value accumulated during the loop
user.totalWeight -= (weightToRemove).toUint248();
// subtracts weight and token value from global variables
globalWeight -= weightToRemove;
// gets token value by dividing by yield weight multiplier
poolTokenReserve -= (weightToRemove) / Stake.YIELD_STAKE_WEIGHT_MULTIPLIER;
// expects the factory to mint ILV yield to the msg.sender user
// after all checks and calculations have been successfully
// executed
_factory.mintYieldTo(msg.sender, amountToMint, false);
// emits an event
emit LogV1YieldMintedMultiple(msg.sender, amountToMint);
}
/**
* @dev Verifies a proof from the yield weights merkle, and if it's valid,
* adds the v1 user yield weight to the v2 `user.totalWeight`.
* @dev The yield weights merkle tree will be published after the initial contracts
* deployment, and then the merkle root is added through `setMerkleRoot` function.
*
* @param _proof bytes32 array with the proof generated off-chain
* @param _index user index in the merkle tree
* @param _yieldWeight user yield weight in v1 stored by the merkle tree
* @param _pendingV1Rewards user pending rewards in v1 stored by the merkle tree
* @param _useSILV whether the user wants rewards in sILV token or in a v2 ILV yield stake
*/
function _migrateYieldWeights(
bytes32[] calldata _proof,
uint256 _index,
uint256 _yieldWeight,
uint256 _pendingV1Rewards,
bool _useSILV
) internal virtual {
// gets storage pointer to the user
User storage user = users[msg.sender];
// bytes4(keccak256("_migrateYieldWeights(bytes32[],uint256,uint256)")))
bytes4 fnSelector = 0x660e5908;
// requires that the user hasn't migrated the yield yet
fnSelector.verifyAccess(!hasMigratedYield(_index));
// compute leaf and verify merkle proof
bytes32 leaf = keccak256(abi.encodePacked(_index, msg.sender, _yieldWeight, _pendingV1Rewards));
// verifies the merkle proof and requires the return value to be true
fnSelector.verifyInput(MerkleProof.verify(_proof, merkleRoot, leaf), 0);
// gets the value compounded into v2 as ILV yield to be added into v2 user.totalWeight
uint256 pendingRewardsCompounded = _migratePendingRewards(_pendingV1Rewards, _useSILV);
uint256 weightCompounded = pendingRewardsCompounded * Stake.YIELD_STAKE_WEIGHT_MULTIPLIER;
uint256 ilvYieldMigrated = _yieldWeight / Stake.YIELD_STAKE_WEIGHT_MULTIPLIER;
// add v1 yield weight to the v2 user
user.totalWeight += (_yieldWeight + weightCompounded).toUint248();
// adds v1 pending yield compounded + v1 total yield to global weight
// and poolTokenReserve in the v2 contract.
globalWeight += (weightCompounded + _yieldWeight);
poolTokenReserve += (pendingRewardsCompounded + ilvYieldMigrated);
// set user as claimed in bitmap
_usersMigrated.set(_index);
// emits an event
emit LogMigrateYieldWeight(msg.sender, _yieldWeight);
}
/**
* @dev Gets pending rewards in the v1 ilv pool and v1 lp pool stored in the merkle tree,
* and allows the v1 users of those pools to claim them as ILV compounded in the v2 pool or
* sILV minted to their wallet.
* @dev Eligible users are filtered and stored in the merkle tree.
*
* @param _pendingV1Rewards user pending rewards in v1 stored by the merkle tree
* @param _useSILV whether the user wants rewards in sILV token or in a v2 ILV yield stake
*
* @return pendingRewardsCompounded returns the value compounded into the v2 pool (if the user selects ILV)
*/
function _migratePendingRewards(uint256 _pendingV1Rewards, bool _useSILV)
internal
virtual
returns (uint256 pendingRewardsCompounded)
{
// gets pointer to user
User storage user = users[msg.sender];
// if the user (msg.sender) wants to mint pending rewards as sILV, simply mint
if (_useSILV) {
// calls the factory to mint sILV
_factory.mintYieldTo(msg.sender, _pendingV1Rewards, _useSILV);
} else {
// otherwise we create a new v2 yield stake (ILV)
Stake.Data memory stake = Stake.Data({
value: (_pendingV1Rewards).toUint120(),
lockedFrom: (_now256()).toUint64(),
lockedUntil: (_now256() + Stake.MAX_STAKE_PERIOD).toUint64(),
isYield: true
});
// adds new ILV yield stake to user array
// notice that further values will be updated later in execution
// (user.totalWeight, user.subYieldRewards, user.subVaultRewards, ...)
user.stakes.push(stake);
// updates function's return value
pendingRewardsCompounded = _pendingV1Rewards;
}
// emits an event
emit LogMigratePendingRewards(msg.sender, _pendingV1Rewards, _useSILV);
}
/**
* @inheritdoc CorePool
* @dev In the ILV Pool we verify that the user isn't coming from v1.
* @dev If user has weight in v1, we can't allow them to call this
* function, otherwise it would throw an error in the new address when calling
* mintV1YieldMultiple if the user migrates.
*/
function moveFundsFromWallet(address _to) public virtual override {
// we're using function selector to simplify validation
bytes4 fnSelector = this.moveFundsFromWallet.selector;
// we query v1 ilv pool contract
(, uint256 totalWeight, , ) = ICorePoolV1(corePoolV1).users(msg.sender);
// we check that the v1 total weight is 0 i.e the user can't have any yield
fnSelector.verifyState(totalWeight == 0, 0);
// call parent moveFundsFromWalet which contains further checks and the actual
// execution
super.moveFundsFromWallet(_to);
}
/**
* @dev Empty reserved space in storage. The size of the __gap array is calculated so that
* the amount of storage used by a contract always adds up to the 50.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[46] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*/
abstract 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 protect an initializer function from being invoked twice.
*/
modifier initializer() {
require(_initializing || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20Upgradeable {
using AddressUpgradeable for address;
function safeTransfer(
IERC20Upgradeable token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20Upgradeable token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20Upgradeable 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'
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(
IERC20Upgradeable token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20Upgradeable token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20Upgradeable 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. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
pragma solidity 0.8.4;
import { ErrorHandler } from "./ErrorHandler.sol";
/**
* @notice Copied from OpenZeppelin's SafeCast.sol, adapted to use just in the required
* uint sizes.
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such 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.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
using ErrorHandler for bytes4;
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 _value) internal pure returns (uint248) {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("toUint248(uint256))"))`
bytes4 fnSelector = 0x3fd72672;
fnSelector.verifyInput(_value <= type(uint248).max, 0);
return uint248(_value);
}
/**
* @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) {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("toUint128(uint256))"))`
bytes4 fnSelector = 0x809fdd33;
fnSelector.verifyInput(_value <= type(uint128).max, 0);
return uint128(_value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 _value) internal pure returns (uint120) {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("toUint120(uint256))"))`
bytes4 fnSelector = 0x1e4e4bad;
fnSelector.verifyInput(_value <= type(uint120).max, 0);
return uint120(_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) {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("toUint64(uint256))"))`
bytes4 fnSelector = 0x2665fad0;
fnSelector.verifyInput(_value <= type(uint64).max, 0);
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) {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("toUint32(uint256))"))`
bytes4 fnSelector = 0xc8193255;
fnSelector.verifyInput(_value <= type(uint32).max, 0);
return uint32(_value);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
* Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
*/
library BitMaps {
struct BitMap {
mapping(uint256 => uint256) _data;
}
/**
* @dev Returns whether the bit at `index` is set.
*/
function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
return bitmap._data[bucket] & mask != 0;
}
/**
* @dev Sets the bit at `index` to the boolean `value`.
*/
function setTo(
BitMap storage bitmap,
uint256 index,
bool value
) internal {
if (value) {
set(bitmap, index);
} else {
unset(bitmap, index);
}
}
/**
* @dev Sets the bit at `index`.
*/
function set(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
bitmap._data[bucket] |= mask;
}
/**
* @dev Unsets the bit at `index`.
*/
function unset(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
bitmap._data[bucket] &= ~mask;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle Trees proofs.
*
* The proofs can be generated using the JavaScript library
* https://github.com/miguelmota/merkletreejs[merkletreejs].
* Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
*
* See `test/utils/cryptography/MerkleProof.test.js` for some examples.
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
// Hash(current element of the proof + current computed hash)
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
// Check if the computed hash (root) is equal to the provided root
return computedHash == root;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { ICorePoolV1 } from "../interfaces/ICorePoolV1.sol";
import { ErrorHandler } from "../libraries/ErrorHandler.sol";
import { Stake } from "../libraries/Stake.sol";
import { CorePool } from "./CorePool.sol";
/**
* @title V2Migrator
*
* @dev V2Migrator inherits all CorePool base contract functionaltiy, and adds
* v1 to v2 migration related functions. This is a core smart contract of
* Sushi LP and ILV pools, and manages users locked and yield weights coming
* from v1.
* @dev Parameters need to be reviewed carefully before deployment for the migration process.
* @dev Users will migrate their locked stakes, which are stored in the contract,
* and v1 total yield weights by data stored in a merkle tree using merkle proofs.
*/
abstract contract V2Migrator is Initializable, CorePool {
using ErrorHandler for bytes4;
using Stake for uint256;
/// @dev Maps v1 addresses that are black listed for v2 migration.
mapping(address => bool) public isBlacklisted;
/// @dev Stores maximum timestamp of a v1 stake (yield) accepted in v2.
uint256 internal _v1StakeMaxPeriod;
/// @dev Stores maximum timestamp of a v1 stake (deposit) accepted in v2.
uint256 internal constant _v1DepositMaxPeriod = 1648150500;
/**
* @dev logs `_migrateLockedStakes()`
*
* @param from user address
* @param totalV1WeightAdded total amount of weight coming from locked stakes in v1
*
*/
event LogMigrateLockedStakes(address indexed from, uint256 totalV1WeightAdded);
/**
* @dev V2Migrator initializer function.
*
* @param v1StakeMaxPeriod_ max timestamp that we accept _lockedFrom values
* in v1 stakes
*/
function __V2Migrator_init(
address ilv_,
address silv_,
address _poolToken,
address _corePoolV1,
address factory_,
uint64 _initTime,
uint32 _weight,
uint256 v1StakeMaxPeriod_
) internal initializer {
// call internal core pool intializar
__CorePool_init(ilv_, silv_, _poolToken, _corePoolV1, factory_, _initTime, _weight);
// sets max period for upgrading to V2 contracts i.e migrating
_v1StakeMaxPeriod = v1StakeMaxPeriod_;
}
/**
* @notice Blacklists a list of v1 user addresses by setting the
* _isBlacklisted flag to true.
*
* @dev The intention is to prevent addresses that exploited v1 to be able to move
* stake ids to the v2 contracts and to be able to mint any yield from a v1
* stake id with the isYield flag set to true.
*
* @param _users v1 users address array
*/
function blacklistUsers(address[] calldata _users) external virtual {
// only the factory controller can blacklist users
_requireIsFactoryController();
// we're using selector to simplify validation
bytes4 fnSelector = this.blacklistUsers.selector;
// gets each user in the array to be blacklisted
for (uint256 i = 0; i < _users.length; i++) {
// makes sure user passed isn't the address 0
fnSelector.verifyInput(_users[i] != address(0), 0);
// updates mapping
isBlacklisted[_users[i]] = true;
}
}
/**
* @dev External migrateLockedStakes call, used in the Sushi LP pool contract.
* @dev The function is used by users that want to migrate locked stakes in v1,
* but have no yield in the pool. This happens in two scenarios:
*
* 1 - The user pool is the Sushi LP pool, which only has stakes;
* 2 - The user joined ILV pool recently, doesn't have much yield and
* doesn't want to migrate their yield weight in the pool;
* @notice Most of the times this function will be used in the inherited Sushi
* LP pool contract (called by the v1 user coming from sushi pool),
* but it's possible that a v1 user coming from the ILV pool decides
* to use this function instead of `executeMigration()` defined in
* the ILV pool contract.
*
* @param _stakeIds array of v1 stake ids
*/
function migrateLockedStakes(uint256[] calldata _stakeIds) external virtual {
// verifies that user isn't a v1 blacklisted user
_requireNotBlacklisted(msg.sender);
// checks if contract is paused
_requireNotPaused();
// uses v1 weight values for rewards calculations
uint256 v1WeightToAdd = _useV1Weight(msg.sender);
// update user state
_updateReward(msg.sender, v1WeightToAdd);
// call internal migrate locked stake function
// which does the loop to store each v1 stake
// reference in v2 and all required data
_migrateLockedStakes(_stakeIds);
}
/**
* @dev Reads v1 core pool locked stakes data (by looping through the `_stakeIds` array),
* checks if it's a valid v1 stake to migrate and save the id to v2 user struct.
*
* @dev Only `msg.sender` can migrate v1 stakes to v2.
*
* @param _stakeIds array of v1 stake ids
*/
function _migrateLockedStakes(uint256[] calldata _stakeIds) internal virtual {
User storage user = users[msg.sender];
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("_migrateLockedStakes(uint256[])"))`
bytes4 fnSelector = 0x80812525;
// initializes variable which will tell how much
// weight in v1 the user is bringing to v2
uint256 totalV1WeightAdded;
// loops over each v1 stake id passed to do the necessary validity checks
// and store the values required in v2 to keep track of v1 weight in order
// to include it in v2 rewards (yield and revenue distribution) calculations
for (uint256 i = 0; i < _stakeIds.length; i++) {
// reads the v1 stake by calling the v1 core pool getDeposit and separates
// all required data in the struct to be used
(, uint256 _weight, uint64 lockedFrom, , bool isYield) = ICorePoolV1(corePoolV1).getDeposit(
msg.sender,
_stakeIds[i]
);
// checks if the v1 stake is in the valid period for migration
fnSelector.verifyState(lockedFrom <= _v1DepositMaxPeriod, i * 3);
// checks if the v1 stake has been locked originally and isn't a yield
// stake, which are the requirements for moving to v2 through this function
fnSelector.verifyState(lockedFrom > 0 && !isYield, i * 3 + 1);
// checks if the user has already brought those v1 stakes to v2
fnSelector.verifyState(v1StakesWeights[msg.sender][_stakeIds[i]] == 0, i * 3 + 2);
// adds v1 weight to the dynamic mapping which will be used in calculations
v1StakesWeights[msg.sender][_stakeIds[i]] = _weight;
// updates the variable keeping track of the total weight migrated
totalV1WeightAdded += _weight;
// update value keeping track of v1 stakes ids mapping length
user.v1IdsLength++;
// adds stake id to mapping keeping track of each v1 stake id
user.v1StakesIds[user.v1IdsLength - 1] = _stakeIds[i];
}
// emits an event
emit LogMigrateLockedStakes(msg.sender, totalV1WeightAdded);
}
/**
* @dev Utility used by functions that can't allow blacklisted users to call.
* @dev Blocks user addresses stored in the _isBlacklisted mapping to call actions like
* minting v1 yield stake ids and migrating locked stakes.
*/
function _requireNotBlacklisted(address _user) internal view virtual {
// we're using selector to simplify input and access validation
bytes4 fnSelector = this.migrateLockedStakes.selector;
// makes sure that msg.sender isn't a blacklisted address
fnSelector.verifyAccess(!isBlacklisted[_user]);
}
/**
* @dev Empty reserved space in storage. The size of the __gap array is calculated so that
* the amount of storage used by a contract always adds up to the 50.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[48] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import { SafeCast } from "../libraries/SafeCast.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import { Timestamp } from "./Timestamp.sol";
import { VaultRecipient } from "./VaultRecipient.sol";
import { ErrorHandler } from "../libraries/ErrorHandler.sol";
import { Stake } from "../libraries/Stake.sol";
import { IILVPool } from "../interfaces/IILVPool.sol";
import { IFactory } from "../interfaces/IFactory.sol";
import { ICorePool } from "../interfaces/ICorePool.sol";
import { ICorePoolV1 } from "../interfaces/ICorePoolV1.sol";
/**
* @title Core Pool
*
* @notice An abstract contract containing common logic for ILV and ILV/ETH SLP pools.
*
* @dev Base smart contract for ILV and LP pool. Stores each pool user by mapping
* its address to the user struct. User struct stores v2 stakes, which fit
* in 1 storage slot each (by using the Stake lib), total weights, pending
* yield and revenue distributions, and v1 stake ids. ILV and LP stakes can
* be made through flexible stake mode, which only increments the flexible
* balance of a given user, or through locked staking. Locked staking creates
* a new Stake element fitting 1 storage slot with its value and lock duration.
* When calculating pending rewards, CorePool checks v1 locked stakes weights
* to increment in the calculations and stores pending yield and pending revenue
* distributions. Every time a stake or unstake related function is called,
* it updates pending values, but don't require instant claimings. Rewards
* claiming are executed in separate functions, and in the case of yield,
* it also requires the user checking whether ILV or sILV is wanted as the yield reward.
*
* @dev Deployment and initialization.
* After proxy is deployed and attached to the implementation, it should be
* registered by the PoolFactory contract
* Additionally, 3 token instance addresses must be defined on deployment:
* - ILV token address
* - sILV token address, used to mint sILV rewards
* - pool token address, it can be ILV token address, ILV/ETH pair address, and others
*
* @dev Pool weight defines the fraction of the yield current pool receives among the other pools,
* pool factory is responsible for the weight synchronization between the pools.
* @dev The weight is logically 20% for ILV pool and 80% for ILV/ETH pool initially.
* It can be changed through ICCPs and new flash pools added in the protocol.
* Since Solidity doesn't support fractions the weight is defined by the division of
* pool weight by total pools weight (sum of all registered pools within the factory).
* @dev For ILV Pool we use 200 as weight and for ILV/ETH SLP pool - 800.
*
*/
abstract contract CorePool is
Initializable,
UUPSUpgradeable,
VaultRecipient,
ReentrancyGuardUpgradeable,
PausableUpgradeable,
Timestamp
{
using SafeERC20Upgradeable for IERC20Upgradeable;
using SafeCast for uint256;
using Stake for Stake.Data;
using ErrorHandler for bytes4;
using Stake for uint256;
/// @dev Data structure representing token holder using a pool.
struct User {
/// @dev pending yield rewards to be claimed
uint128 pendingYield;
/// @dev pending revenue distribution to be claimed
uint128 pendingRevDis;
/// @dev Total weight
uint248 totalWeight;
/// @dev number of v1StakesIds
uint8 v1IdsLength;
/// @dev Checkpoint variable for yield calculation
uint256 yieldRewardsPerWeightPaid;
/// @dev Checkpoint variable for vault rewards calculation
uint256 vaultRewardsPerWeightPaid;
/// @dev An array of holder's stakes
Stake.Data[] stakes;
/// @dev A mapping of holder's stakes ids in V1
mapping(uint256 => uint256) v1StakesIds;
}
/// @dev Data structure used in `unstakeLockedMultiple()` function.
struct UnstakeParameter {
uint256 stakeId;
uint256 value;
}
/// @dev Token holder storage, maps token holder address to their data record.
mapping(address => User) public users;
/// @dev Maps `keccak256(userAddress,stakeId)` to a uint256 value that tells
/// a v1 locked stake weight that has already been migrated to v2
/// and is updated through _useV1Weight.
mapping(address => mapping(uint256 => uint256)) public v1StakesWeights;
/// @dev Link to sILV ERC20 Token instance.
address internal _silv;
/// @dev Link to ILV ERC20 Token instance.
address internal _ilv;
/// @dev Address of v1 core pool with same poolToken.
address internal corePoolV1;
/// @dev Link to the pool token instance, for example ILV or ILV/ETH pair.
address public poolToken;
/// @dev Pool weight, initial values are 200 for ILV pool and 800 for ILV/ETH.
uint32 public weight;
/// @dev Timestamp of the last yield distribution event.
uint64 public lastYieldDistribution;
/// @dev Used to calculate yield rewards.
/// @dev This value is different from "reward per token" used in flash pool.
/// @dev Note: stakes are different in duration and "weight" reflects that.
uint256 public yieldRewardsPerWeight;
/// @dev Used to calculate rewards, keeps track of the tokens weight locked in staking.
uint256 public globalWeight;
/// @dev Used to calculate rewards, keeps track of the correct token weight in the v1
/// core pool.
uint256 public v1GlobalWeight;
/// @dev Pool tokens value available in the pool;
/// pool token examples are ILV (ILV core pool) or ILV/ETH pair (LP core pool).
/// @dev For LP core pool this value doesnt' count for ILV tokens received as Vault rewards
/// while for ILV core pool it does count for such tokens as well.
uint256 public poolTokenReserve;
/// @dev Flag indicating pool type, false means "core pool".
bool public constant isFlashPool = false;
/**
* @dev Fired in _stake() and stakeAsPool() in ILVPool contract.
* @param by address that executed the stake function (user or pool)
* @param from token holder address, the tokens will be returned to that address
* @param stakeId id of the new stake created
* @param value value of tokens staked
* @param lockUntil timestamp indicating when tokens should unlock (max 2 years)
*/
event LogStake(address indexed by, address indexed from, uint256 stakeId, uint256 value, uint64 lockUntil);
/**
* @dev Fired in `unstakeLocked()`.
*
* @param to address receiving the tokens (user)
* @param stakeId id value of the stake
* @param value number of tokens unstaked
* @param isYield whether stake struct unstaked was coming from yield or not
*/
event LogUnstakeLocked(address indexed to, uint256 stakeId, uint256 value, bool isYield);
/**
* @dev Fired in `unstakeLockedMultiple()`.
*
* @param to address receiving the tokens (user)
* @param totalValue total number of tokens unstaked
* @param unstakingYield whether unstaked tokens had isYield flag true or false
*/
event LogUnstakeLockedMultiple(address indexed to, uint256 totalValue, bool unstakingYield);
/**
* @dev Fired in `_sync()`, `sync()` and dependent functions (stake, unstake, etc.).
*
* @param by an address which performed an operation
* @param yieldRewardsPerWeight updated yield rewards per weight value
* @param lastYieldDistribution usually, current timestamp
*/
event LogSync(address indexed by, uint256 yieldRewardsPerWeight, uint64 lastYieldDistribution);
/**
* @dev Fired in `_claimYieldRewards()`.
*
* @param by an address which claimed the rewards (staker or ilv pool contract
* in case of a multiple claim call)
* @param from an address which received the yield
* @param sILV flag indicating if reward was paid (minted) in sILV
* @param value value of yield paid
*/
event LogClaimYieldRewards(address indexed by, address indexed from, bool sILV, uint256 value);
/**
* @dev Fired in `_claimVaultRewards()`.
*
* @param by an address which claimed the rewards (staker or ilv pool contract
* in case of a multiple claim call)
* @param from an address which received the yield
* @param value value of yield paid
*/
event LogClaimVaultRewards(address indexed by, address indexed from, uint256 value);
/**
* @dev Fired in `_updateRewards()`.
*
* @param by an address which processed the rewards (staker or ilv pool contract
* in case of a multiple claim call)
* @param from an address which received the yield
* @param yieldValue value of yield processed
* @param revDisValue value of revenue distribution processed
*/
event LogUpdateRewards(address indexed by, address indexed from, uint256 yieldValue, uint256 revDisValue);
/**
* @dev fired in `moveFundsFromWallet()`.
*
* @param from user asking migration
* @param to new user address
* @param previousTotalWeight total weight of `from` before moving to a new address
* @param newTotalWeight total weight of `to` after moving to a new address
* @param previousYield pending yield of `from` before moving to a new address
* @param newYield pending yield of `to` after moving to a new address
* @param previousRevDis pending revenue distribution of `from` before moving to a new address
* @param newRevDis pending revenue distribution of `to` after moving to a new address
*/
event LogMoveFundsFromWallet(
address indexed from,
address indexed to,
uint248 previousTotalWeight,
uint248 newTotalWeight,
uint128 previousYield,
uint128 newYield,
uint128 previousRevDis,
uint128 newRevDis
);
/**
* @dev Fired in `receiveVaultRewards()`.
*
* @param by an address that sent the rewards, always a vault
* @param value amount of tokens received
*/
event LogReceiveVaultRewards(address indexed by, uint256 value);
/**
* @dev Used in child contracts to initialize the pool.
*
* @param ilv_ ILV ERC20 Token address
* @param silv_ sILV ERC20 Token address
* @param _poolToken token the pool operates on, for example ILV or ILV/ETH pair
* @param _corePoolV1 v1 core pool address
* @param factory_ PoolFactory contract address
* @param _initTime initial timestamp used to calculate the rewards
* note: _initTime is set to the future effectively meaning _sync() calls will do nothing
* before _initTime
* @param _weight number representing the pool's weight, which in _sync calls
* is used by checking the total pools weight in the PoolFactory contract
*/
function __CorePool_init(
address ilv_,
address silv_,
address _poolToken,
address _corePoolV1,
address factory_,
uint64 _initTime,
uint32 _weight
) internal initializer {
// we're using selector to simplify input and state validation
// internal function simulated selector is
// `bytes4(keccak256("__CorePool_init(address,address,address,address,address,uint64,uint32)"))`
bytes4 fnSelector = 0x1512be06;
// verify the inputs
fnSelector.verifyNonZeroInput(uint160(_poolToken), 2);
fnSelector.verifyNonZeroInput(uint160(_corePoolV1), 3);
fnSelector.verifyNonZeroInput(_initTime, 5);
fnSelector.verifyNonZeroInput(_weight, 6);
__FactoryControlled_init(factory_);
__ReentrancyGuard_init();
__Pausable_init();
// save the inputs into internal state variables
_ilv = ilv_;
_silv = silv_;
poolToken = _poolToken;
corePoolV1 = _corePoolV1;
weight = _weight;
// init the dependent internal state variables
lastYieldDistribution = _initTime;
}
/**
* @notice Calculates current yield rewards value available for address specified.
*
* @dev See `_pendingRewards()` for further details.
*
* @dev External `pendingRewards()` returns pendingYield and pendingRevDis
* accumulated with already stored user.pendingYield and user.pendingRevDis.
*
* @param _staker an address to calculate yield rewards value for
*/
function pendingRewards(address _staker)
external
view
virtual
returns (uint256 pendingYield, uint256 pendingRevDis)
{
this.pendingRewards.selector.verifyNonZeroInput(uint160(_staker), 0);
// `newYieldRewardsPerWeight` will be the stored or recalculated value for `yieldRewardsPerWeight`
uint256 newYieldRewardsPerWeight;
// gas savings
uint256 _lastYieldDistribution = lastYieldDistribution;
// based on the rewards per weight value, calculate pending rewards;
User storage user = users[_staker];
// initializes both variables from one storage slot
(uint256 v1StakesLength, uint256 userWeight) = (uint256(user.v1IdsLength), uint256(user.totalWeight));
// total user v1 weight to be used
uint256 totalV1Weight;
if (v1StakesLength > 0) {
// loops through v1StakesIds and adds v1 weight
for (uint256 i = 0; i < v1StakesLength; i++) {
uint256 stakeId = user.v1StakesIds[i];
(, uint256 _weight, , , ) = ICorePoolV1(corePoolV1).getDeposit(_staker, stakeId);
uint256 storedWeight = v1StakesWeights[_staker][stakeId];
totalV1Weight += _weight <= storedWeight ? _weight : storedWeight;
}
}
// if smart contract state was not updated recently, `yieldRewardsPerWeight` value
// is outdated and we need to recalculate it in order to calculate pending rewards correctly
if (_now256() > _lastYieldDistribution && globalWeight != 0) {
uint256 endTime = _factory.endTime();
uint256 multiplier = _now256() > endTime
? endTime - _lastYieldDistribution
: _now256() - _lastYieldDistribution;
uint256 ilvRewards = (multiplier * weight * _factory.ilvPerSecond()) / _factory.totalWeight();
// recalculated value for `yieldRewardsPerWeight`
newYieldRewardsPerWeight =
ilvRewards.getRewardPerWeight((globalWeight + v1GlobalWeight)) +
yieldRewardsPerWeight;
} else {
// if smart contract state is up to date, we don't recalculate
newYieldRewardsPerWeight = yieldRewardsPerWeight;
}
pendingYield =
(userWeight + totalV1Weight).earned(newYieldRewardsPerWeight, user.yieldRewardsPerWeightPaid) +
user.pendingYield;
pendingRevDis =
(userWeight + totalV1Weight).earned(vaultRewardsPerWeight, user.vaultRewardsPerWeightPaid) +
user.pendingRevDis;
}
/**
* @notice Returns total staked token balance for the given address.
* @dev Loops through stakes and returns total balance.
* @notice Expected to be called externally through `eth_call`. Gas shouldn't
* be an issue here.
*
* @param _user an address to query balance for
* @return balance total staked token balance
*/
function balanceOf(address _user) external view virtual returns (uint256 balance) {
// gets storage pointer to _user
User storage user = users[_user];
// loops over each user stake and adds to the total balance.
for (uint256 i = 0; i < user.stakes.length; i++) {
balance += user.stakes[i].value;
}
}
/**
* @dev Returns the sum of poolTokenReserve with the deposit reserves in v1.
* @dev In ILV Pool contract the eDAO stores the v1 reserve value, and
* in the SLP pool we're able to query it from the v1 lp pool contract.
*/
function getTotalReserves() external view virtual returns (uint256 totalReserves);
/**
* @notice Returns information on the given stake for the given address.
*
* @dev See getStakesLength.
*
* @param _user an address to query stake for
* @param _stakeId zero-indexed stake ID for the address specified
* @return stake info as Stake structure
*/
function getStake(address _user, uint256 _stakeId) external view virtual returns (Stake.Data memory) {
// read stake at specified index and return
return users[_user].stakes[_stakeId];
}
/**
* @notice Returns a v1 stake id in the `user.v1StakesIds` array.
*
* @dev Get v1 stake id position through `getV1StakePosition()`.
*
* @param _user an address to query stake for
* @param _position position index in the array
* @return stakeId value
*/
function getV1StakeId(address _user, uint256 _position) external view virtual returns (uint256) {
// returns the v1 stake id indicated at _position value
return users[_user].v1StakesIds[_position];
}
/**
* @notice Returns a v1 stake position in the `user.v1StakesIds` array.
*
* @dev Helper function to call `getV1StakeId()`.
* @dev Reverts if stakeId isn't found.
*
* @param _user an address to query stake for
* @param _desiredId desired stakeId position in the array to find
* @return position stake info as Stake structure
*/
function getV1StakePosition(address _user, uint256 _desiredId) external view virtual returns (uint256 position) {
// gets storage pointer to user
User storage user = users[_user];
// loops over each v1 stake id and checks if it's the one
// that the caller is looking for
for (uint256 i = 0; i < user.v1IdsLength; i++) {
if (user.v1StakesIds[i] == _desiredId) {
// if it's the desired stake id, return the array index (i.e position)
return i;
}
}
revert();
}
/**
* @notice Returns number of stakes for the given address. Allows iteration over stakes.
*
* @dev See `getStake()`.
*
* @param _user an address to query stake length for
* @return number of stakes for the given address
*/
function getStakesLength(address _user) external view virtual returns (uint256) {
// read stakes array length and return
return users[_user].stakes.length;
}
/**
* @dev Set paused/unpaused state in the pool contract.
*
* @param _shouldPause whether the contract should be paused/unpausd
*/
function pause(bool _shouldPause) external {
// checks if caller is authorized to pause
_requireIsFactoryController();
// checks bool input and pause/unpause the contract depending on
// msg.sender's request
if (_shouldPause) {
_pause();
} else {
_unpause();
}
}
/**
* @notice Stakes specified value of tokens for the specified value of time,
* and pays pending yield rewards if any.
*
* @dev Requires value to stake and lock duration to be greater than zero.
*
* @param _value value of tokens to stake
* @param _lockDuration stake duration as unix timestamp
*/
function stake(uint256 _value, uint64 _lockDuration) external virtual nonReentrant {
// checks if the contract is in a paused state
_requireNotPaused();
// we're using selector to simplify input and state validation
bytes4 fnSelector = this.stake.selector;
// validate the inputs
fnSelector.verifyNonZeroInput(_value, 1);
fnSelector.verifyInput(_lockDuration >= Stake.MIN_STAKE_PERIOD && _lockDuration <= Stake.MAX_STAKE_PERIOD, 2);
// get a link to user data struct, we will write to it later
User storage user = users[msg.sender];
// uses v1 weight values for rewards calculations
uint256 v1WeightToAdd = _useV1Weight(msg.sender);
// update user state
_updateReward(msg.sender, v1WeightToAdd);
// calculates until when a stake is going to be locked
uint64 lockUntil = (_now256()).toUint64() + _lockDuration;
// stake weight formula rewards for locking
uint256 stakeWeight = (((lockUntil - _now256()) * Stake.WEIGHT_MULTIPLIER) /
Stake.MAX_STAKE_PERIOD +
Stake.BASE_WEIGHT) * _value;
// makes sure stakeWeight is valid
assert(stakeWeight > 0);
// create and save the stake (append it to stakes array)
Stake.Data memory userStake = Stake.Data({
value: (_value).toUint120(),
lockedFrom: (_now256()).toUint64(),
lockedUntil: lockUntil,
isYield: false
});
// pushes new stake to `stakes` array
user.stakes.push(userStake);
// update user weight
user.totalWeight += (stakeWeight).toUint248();
// update global weight value and global pool token count
globalWeight += stakeWeight;
poolTokenReserve += _value;
// transfer `_value`
IERC20Upgradeable(poolToken).safeTransferFrom(address(msg.sender), address(this), _value);
// emit an event
emit LogStake(msg.sender, msg.sender, (user.stakes.length - 1), _value, lockUntil);
}
/**
* @dev Moves msg.sender stake data to a new address.
* @dev V1 stakes are never migrated to the new address. We process all rewards,
* clean the previous user (msg.sender), add the previous user data to
* the desired address and update subYieldRewards/subVaultRewards values
* in order to make sure both addresses will have rewards cleaned.
*
* @param _to new user address, needs to be a fresh address with no stakes
*/
function moveFundsFromWallet(address _to) public virtual {
// checks if the contract is in a paused state
_requireNotPaused();
// gets storage pointer to msg.sender user struct
User storage previousUser = users[msg.sender];
// gets storage pointer to desired address user struct
User storage newUser = users[_to];
// uses v1 weight values for rewards calculations
uint256 v1WeightToAdd = _useV1Weight(msg.sender);
// We process update global and user's rewards
// before moving the user funds to a new wallet.
// This way we can ensure that all v1 ids weight have been used before the v2
// stakes to a new address.
_updateReward(msg.sender, v1WeightToAdd);
// we're using selector to simplify input and state validation
bytes4 fnSelector = this.moveFundsFromWallet.selector;
// validate input is set
fnSelector.verifyNonZeroInput(uint160(_to), 0);
// verify new user records are empty
fnSelector.verifyState(
newUser.totalWeight == 0 &&
newUser.v1IdsLength == 0 &&
newUser.stakes.length == 0 &&
newUser.yieldRewardsPerWeightPaid == 0 &&
newUser.vaultRewardsPerWeightPaid == 0,
0
);
// saves previous user total weight
uint248 previousTotalWeight = previousUser.totalWeight;
// saves previous user pending yield
uint128 previousYield = previousUser.pendingYield;
// saves previous user pending rev dis
uint128 previousRevDis = previousUser.pendingRevDis;
// It's expected to have all previous user values
// migrated to the new user address (_to).
// We recalculate yield and vault rewards values
// to make sure new user pending yield and pending rev dis to be stored
// at newUser.pendingYield and newUser.pendingRevDis is 0, since we just processed
// all pending rewards calling _updateReward.
newUser.totalWeight = previousTotalWeight;
newUser.pendingYield = previousYield;
newUser.pendingRevDis = previousRevDis;
newUser.yieldRewardsPerWeightPaid = yieldRewardsPerWeight;
newUser.vaultRewardsPerWeightPaid = vaultRewardsPerWeight;
newUser.stakes = previousUser.stakes;
delete previousUser.totalWeight;
delete previousUser.pendingYield;
delete previousUser.pendingRevDis;
delete previousUser.stakes;
// emits an event
emit LogMoveFundsFromWallet(
msg.sender,
_to,
previousTotalWeight,
newUser.totalWeight,
previousYield,
newUser.pendingYield,
previousRevDis,
newUser.pendingRevDis
);
}
/**
* @notice Service function to synchronize pool state with current time.
*
* @dev Can be executed by anyone at any time, but has an effect only when
* at least one second passes between synchronizations.
* @dev Executed internally when staking, unstaking, processing rewards in order
* for calculations to be correct and to reflect state progress of the contract.
* @dev When timing conditions are not met (executed too frequently, or after factory
* end time), function doesn't throw and exits silently.
*/
function sync() external virtual {
_requireNotPaused();
// calls internal function
_sync();
}
/**
* @dev Calls internal `_claimYieldRewards()` passing `msg.sender` as `_staker`.
*
* @notice Pool state is updated before calling the internal function.
*/
function claimYieldRewards(bool _useSILV) external virtual {
// checks if the contract is in a paused state
_requireNotPaused();
// calls internal function
_claimYieldRewards(msg.sender, _useSILV);
}
/**
* @dev Calls internal `_claimVaultRewards()` passing `msg.sender` as `_staker`.
*
* @notice Pool state is updated before calling the internal function.
*/
function claimVaultRewards() external virtual {
// checks if the contract is in a paused state
_requireNotPaused();
// calls internal function
_claimVaultRewards(msg.sender);
}
/**
* @dev Claims both revenue distribution and yield rewards in one call.
*
*/
function claimAllRewards(bool _useSILV) external virtual {
// checks if the contract is in a paused state
_requireNotPaused();
// calls internal yield and vault rewards functions
_claimVaultRewards(msg.sender);
_claimYieldRewards(msg.sender, _useSILV);
}
/**
* @dev Executed by the vault to transfer vault rewards ILV from the vault
* into the pool.
*
* @dev This function is executed only for ILV core pools.
*
* @param _value amount of ILV rewards to transfer into the pool
*/
function receiveVaultRewards(uint256 _value) external virtual {
// always sync the pool state vars before moving forward
_sync();
// checks if the contract is in a paused state
_requireNotPaused();
// checks if msg.sender is the vault contract
_requireIsVault();
// we're using selector to simplify input and state validation
bytes4 fnSelector = this.receiveVaultRewards.selector;
// return silently if there is no reward to receive
if (_value == 0) {
return;
}
// verify weight is not zero
fnSelector.verifyState(globalWeight > 0 || v1GlobalWeight > 0, 0);
// we update vaultRewardsPerWeight value using v1 and v2 global weight,
// expecting to distribute revenue distribution correctly to all users
// coming from v1 and new v2 users.
vaultRewardsPerWeight += _value.getRewardPerWeight(globalWeight + v1GlobalWeight);
// transfers ILV from the Vault contract to the pool
IERC20Upgradeable(_ilv).safeTransferFrom(msg.sender, address(this), _value);
// emits an event
emit LogReceiveVaultRewards(msg.sender, _value);
}
/**
* @dev Updates value that keeps track of v1 global locked tokens weight.
*
* @param _v1GlobalWeight new value to be stored
*/
function setV1GlobalWeight(uint256 _v1GlobalWeight) external virtual {
// only factory controller can update the _v1GlobalWeight
_requireIsFactoryController();
// update v1GlobalWeight state variable
v1GlobalWeight = _v1GlobalWeight;
}
/**
* @dev Executed by the factory to modify pool weight; the factory is expected
* to keep track of the total pools weight when updating.
*
* @dev Set weight to zero to disable the pool.
*
* @param _weight new weight to set for the pool
*/
function setWeight(uint32 _weight) external virtual {
// update pool state using current weight value
_sync();
// verify function is executed by the factory
this.setWeight.selector.verifyAccess(msg.sender == address(_factory));
// set the new weight value
weight = _weight;
}
/**
* @dev Unstakes a stake that has been previously locked, and is now in an unlocked
* state. If the stake has the isYield flag set to true, then the contract
* requests ILV to be minted by the PoolFactory. Otherwise it transfers ILV or LP
* from the contract balance.
*
* @param _stakeId stake ID to unstake from, zero-indexed
* @param _value value of tokens to unstake
*/
function unstake(uint256 _stakeId, uint256 _value) external virtual {
// checks if the contract is in a paused state
_requireNotPaused();
// we're using selector to simplify input and state validation
bytes4 fnSelector = this.unstake.selector;
// verify a value is set
fnSelector.verifyNonZeroInput(_value, 0);
// get a link to user data struct, we will write to it later
User storage user = users[msg.sender];
// get a link to the corresponding stake, we may write to it later
Stake.Data storage userStake = user.stakes[_stakeId];
// checks if stake is unlocked already
fnSelector.verifyState(_now256() > userStake.lockedUntil, 0);
// stake structure may get deleted, so we save isYield flag to be able to use it
// we also save stakeValue for gasSavings
(uint120 stakeValue, bool isYield) = (userStake.value, userStake.isYield);
// verify available balance
fnSelector.verifyInput(stakeValue >= _value, 1);
// uses v1 weight values for rewards calculations
uint256 v1WeightToAdd = _useV1Weight(msg.sender);
// and process current pending rewards if any
_updateReward(msg.sender, v1WeightToAdd);
// store stake weight
uint256 previousWeight = userStake.weight();
// value used to save new weight after updates in storage
uint256 newWeight;
// update the stake, or delete it if its depleted
if (stakeValue - _value == 0) {
// deletes stake struct, no need to save new weight because it stays 0
delete user.stakes[_stakeId];
} else {
userStake.value -= (_value).toUint120();
// saves new weight to memory
newWeight = userStake.weight();
}
// update user record
user.totalWeight = uint248(user.totalWeight - previousWeight + newWeight);
// update global weight variable
globalWeight = globalWeight - previousWeight + newWeight;
// update global pool token count
poolTokenReserve -= _value;
// if the stake was created by the pool itself as a yield reward
if (isYield) {
// mint the yield via the factory
_factory.mintYieldTo(msg.sender, _value, false);
} else {
// otherwise just return tokens back to holder
IERC20Upgradeable(poolToken).safeTransfer(msg.sender, _value);
}
// emits an event
emit LogUnstakeLocked(msg.sender, _stakeId, _value, isYield);
}
/**
* @dev Executes unstake on multiple stakeIds. See `unstakeLocked()`.
* @dev Optimizes gas by requiring all unstakes to be made either in yield stakes
* or in non yield stakes. That way we can transfer or mint tokens in one call.
*
* @notice User is required to either mint ILV or unstake pool tokens in the function call.
* There's no way to do both operations in one call.
*
* @param _stakes array of stakeIds and values to be unstaked in each stake from
* the msg.sender
* @param _unstakingYield whether all stakeIds have isYield flag set to true or false,
* i.e if we're minting ILV or transferring pool tokens
*/
function unstakeMultiple(UnstakeParameter[] calldata _stakes, bool _unstakingYield) external virtual {
// checks if the contract is in a paused state
_requireNotPaused();
// we're using selector to simplify input and state validation
bytes4 fnSelector = this.unstakeMultiple.selector;
// verifies if user has passed any value to be unstaked
fnSelector.verifyNonZeroInput(_stakes.length, 0);
// gets storage pointer to the user
User storage user = users[msg.sender];
// uses v1 weight values for rewards calculations
uint256 v1WeightToAdd = _useV1Weight(msg.sender);
_updateReward(msg.sender, v1WeightToAdd);
// initialize variables that expect to receive the total
// weight to be removed from the user and the value to be
// unstaked from the pool.
uint256 weightToRemove;
uint256 valueToUnstake;
for (uint256 i = 0; i < _stakes.length; i++) {
// destructure calldata parameters
(uint256 _stakeId, uint256 _value) = (_stakes[i].stakeId, _stakes[i].value);
Stake.Data storage userStake = user.stakes[_stakeId];
// checks if stake is unlocked already
fnSelector.verifyState(_now256() > userStake.lockedUntil, i * 3);
// checks if unstaking value is valid
fnSelector.verifyNonZeroInput(_value, 1);
// stake structure may get deleted, so we save isYield flag to be able to use it
// we also save stakeValue for gas savings
(uint120 stakeValue, bool isYield) = (userStake.value, userStake.isYield);
// verifies if the selected stake is yield (i.e ILV to be minted)
// or not, the function needs to either mint yield or transfer tokens
// and can't do both operations at the same time.
fnSelector.verifyState(isYield == _unstakingYield, i * 3 + 1);
// checks if there's enough tokens to unstake
fnSelector.verifyState(stakeValue >= _value, i * 3 + 2);
// store stake weight
uint256 previousWeight = userStake.weight();
// value used to save new weight after updates in storage
uint256 newWeight;
// update the stake, or delete it if its depleted
if (stakeValue - _value == 0) {
// deletes stake struct, no need to save new weight because it stays 0
delete user.stakes[_stakeId];
} else {
// removes _value from the stake with safe cast
userStake.value -= (_value).toUint120();
// saves new weight to memory
newWeight = userStake.weight();
}
// updates the values initialized earlier with the amounts that
// need to be subtracted (weight) and transferred (value to unstake)
weightToRemove += previousWeight - newWeight;
valueToUnstake += _value;
}
// subtracts weight
user.totalWeight -= (weightToRemove).toUint248();
// update global variable
globalWeight -= weightToRemove;
// update pool token count
poolTokenReserve -= valueToUnstake;
// if the stake was created by the pool itself as a yield reward
if (_unstakingYield) {
// mint the yield via the factory
_factory.mintYieldTo(msg.sender, valueToUnstake, false);
} else {
// otherwise just return tokens back to holder
IERC20Upgradeable(poolToken).safeTransfer(msg.sender, valueToUnstake);
}
// emits an event
emit LogUnstakeLockedMultiple(msg.sender, valueToUnstake, _unstakingYield);
}
/**
* @dev Used internally, mostly by children implementations, see `sync()`.
*
* @dev Updates smart contract state (`yieldRewardsPerWeight`, `lastYieldDistribution`),
* updates factory state via `updateILVPerSecond`
*/
function _sync() internal virtual {
// gas savings
IFactory factory_ = _factory;
// update ILV per second value in factory if required
if (factory_.shouldUpdateRatio()) {
factory_.updateILVPerSecond();
}
// check bound conditions and if these are not met -
// exit silently, without emitting an event
uint256 endTime = factory_.endTime();
if (lastYieldDistribution >= endTime) {
return;
}
if (_now256() <= lastYieldDistribution) {
return;
}
// if locking weight is zero - update only `lastYieldDistribution` and exit
if (globalWeight == 0 && v1GlobalWeight == 0) {
lastYieldDistribution = (_now256()).toUint64();
return;
}
// to calculate the reward we need to know how many seconds passed, and reward per second
uint256 currentTimestamp = _now256() > endTime ? endTime : _now256();
uint256 secondsPassed = currentTimestamp - lastYieldDistribution;
uint256 ilvPerSecond = factory_.ilvPerSecond();
// calculate the reward
uint256 ilvReward = (secondsPassed * ilvPerSecond * weight) / factory_.totalWeight();
// update rewards per weight and `lastYieldDistribution`
yieldRewardsPerWeight += ilvReward.getRewardPerWeight((globalWeight + v1GlobalWeight));
lastYieldDistribution = (currentTimestamp).toUint64();
// emit an event
emit LogSync(msg.sender, yieldRewardsPerWeight, lastYieldDistribution);
}
/**
* @dev claims all pendingYield from _staker using ILV or sILV.
*
* @notice sILV is minted straight away to _staker wallet, ILV is created as
* a new stake and locked for Stake.MAX_STAKE_PERIOD.
*
* @param _staker user address
* @param _useSILV whether the user wants to claim ILV or sILV
*/
function _claimYieldRewards(address _staker, bool _useSILV) internal virtual {
// get link to a user data structure, we will write into it later
User storage user = users[_staker];
// uses v1 weight values for rewards calculations
uint256 v1WeightToAdd = _useV1Weight(_staker);
// update user state
_updateReward(_staker, v1WeightToAdd);
// check pending yield rewards to claim and save to memory
uint256 pendingYieldToClaim = uint256(user.pendingYield);
// if pending yield is zero - just return silently
if (pendingYieldToClaim == 0) return;
// clears user pending yield
user.pendingYield = 0;
// if sILV is requested
if (_useSILV) {
// - mint sILV
_factory.mintYieldTo(_staker, pendingYieldToClaim, true);
} else if (poolToken == _ilv) {
// calculate pending yield weight,
// 2e6 is the bonus weight when staking for 1 year
uint256 stakeWeight = pendingYieldToClaim * Stake.YIELD_STAKE_WEIGHT_MULTIPLIER;
// if the pool is ILV Pool - create new ILV stake
// and save it - push it into stakes array
Stake.Data memory newStake = Stake.Data({
value: (pendingYieldToClaim).toUint120(),
lockedFrom: (_now256()).toUint64(),
lockedUntil: (_now256() + Stake.MAX_STAKE_PERIOD).toUint64(), // staking yield for 1 year
isYield: true
});
// add memory stake to storage
user.stakes.push(newStake);
// updates total user weight with the newly created stake's weight
user.totalWeight += (stakeWeight).toUint248();
// update global variable
globalWeight += stakeWeight;
// update reserve count
poolTokenReserve += pendingYieldToClaim;
} else {
// for other pools - stake as pool
address ilvPool = _factory.getPoolAddress(_ilv);
IILVPool(ilvPool).stakeAsPool(_staker, pendingYieldToClaim);
}
// emits an event
emit LogClaimYieldRewards(msg.sender, _staker, _useSILV, pendingYieldToClaim);
}
/**
* @dev Claims all pendingRevDis from _staker using ILV.
* @dev ILV is sent straight away to _staker address.
*
* @param _staker user address
*/
function _claimVaultRewards(address _staker) internal virtual {
// get link to a user data structure, we will write into it later
User storage user = users[_staker];
// uses v1 weight values for rewards calculations
uint256 v1WeightToAdd = _useV1Weight(_staker);
// update user state
_updateReward(_staker, v1WeightToAdd);
// check pending yield rewards to claim and save to memory
uint256 pendingRevDis = uint256(user.pendingRevDis);
// if pending yield is zero - just return silently
if (pendingRevDis == 0) return;
// clears user pending revenue distribution
user.pendingRevDis = 0;
IERC20Upgradeable(_ilv).safeTransfer(_staker, pendingRevDis);
// emits an event
emit LogClaimVaultRewards(msg.sender, _staker, pendingRevDis);
}
/**
* @dev Calls CorePoolV1 contract, gets v1 stake ids weight and returns.
* @dev Used by `_pendingRewards()` to calculate yield and revenue distribution
* rewards taking v1 weights into account.
*
* @notice If v1 weights have changed since last call, we use latest v1 weight for
* yield and revenue distribution rewards calculations, and recalculate
* user sub rewards values in order to have correct rewards estimations.
*
* @param _staker user address passed
*
* @return totalV1Weight uint256 value of v1StakesIds weights
*/
function _useV1Weight(address _staker) internal virtual returns (uint256 totalV1Weight) {
// gets user storage pointer
User storage user = users[_staker];
// gas savings
uint256 v1StakesLength = user.v1IdsLength;
// checks if user has any migrated stake from v1
if (v1StakesLength > 0) {
// loops through v1StakesIds and adds v1 weight
for (uint256 i = 0; i < v1StakesLength; i++) {
// saves v1 stake id to memory
uint256 stakeId = user.v1StakesIds[i];
(, uint256 _weight, , , ) = ICorePoolV1(corePoolV1).getDeposit(_staker, stakeId);
// gets weight stored initially in the v1StakesWeights mapping
// through V2Migrator contract
uint256 storedWeight = v1StakesWeights[_staker][stakeId];
// only stores the current v1 weight that is going to be used for calculations
// if current v1 weight is equal to or less than the stored weight.
// This way we make sure that v1 weight never increases for any reason
// (e.g increasing a v1 stake lock through v1 contract) and messes up calculations.
totalV1Weight += _weight <= storedWeight ? _weight : storedWeight;
// if _weight has updated in v1 to a lower value, we also update
// stored weight in v2 for next calculations
if (storedWeight > _weight) {
// if deposit has been completely unstaked in v1, set stake id weight to 1
// so we can keep track that it has been already migrated.
// otherwise just update value to _weight
v1StakesWeights[_staker][stakeId] = _weight == 0 ? 1 : _weight;
}
}
}
}
/**
* @dev Checks if pool is paused.
* @dev We use this internal function instead of the modifier coming from
* Pausable contract in order to decrease contract's bytecode size.
*/
function _requireNotPaused() internal view virtual {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("_requireNotPaused()"))`
bytes4 fnSelector = 0xabb87a6f;
// checks paused variable value from Pausable Open Zeppelin
fnSelector.verifyState(!paused(), 0);
}
/**
* @dev Must be called every time user.totalWeight is changed.
* @dev Syncs the global pool state, processes the user pending rewards (if any),
* and updates check points values stored in the user struct.
* @dev If user is coming from v1 pool, it expects to receive this v1 user weight
* to include in rewards calculations.
*
* @param _staker user address
* @param _v1WeightToAdd v1 weight to be added to calculations
*/
function _updateReward(address _staker, uint256 _v1WeightToAdd) internal virtual {
// update pool state
_sync();
// gets storage reference to the user
User storage user = users[_staker];
// gas savings
uint256 userTotalWeight = uint256(user.totalWeight) + _v1WeightToAdd;
// calculates pending yield to be added
uint256 pendingYield = userTotalWeight.earned(yieldRewardsPerWeight, user.yieldRewardsPerWeightPaid);
// calculates pending reenue distribution to be added
uint256 pendingRevDis = userTotalWeight.earned(vaultRewardsPerWeight, user.vaultRewardsPerWeightPaid);
// increases stored user.pendingYield with value returned
user.pendingYield += pendingYield.toUint128();
// increases stored user.pendingRevDis with value returned
user.pendingRevDis += pendingRevDis.toUint128();
// updates user checkpoint values for future calculations
user.yieldRewardsPerWeightPaid = yieldRewardsPerWeight;
user.vaultRewardsPerWeightPaid = vaultRewardsPerWeight;
// emit an event
emit LogUpdateRewards(msg.sender, _staker, pendingYield, pendingRevDis);
}
/**
* @dev See UUPSUpgradeable `_authorizeUpgrade()`.
* @dev Just checks if `msg.sender` == `factory.owner()` i.e eDAO multisig address.
* @dev eDAO multisig is responsible by handling upgrades and executing other
* admin actions approved by the Council.
*/
function _authorizeUpgrade(address) internal view virtual override {
// checks caller is factory.owner()
_requireIsFactoryController();
}
/**
* @dev Empty reserved space in storage. The size of the __gap array is calculated so that
* the amount of storage used by a contract always adds up to the 50.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[39] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
/**
* @title Errors Library.
*
* @notice Introduces some very common input and state validation for smart contracts,
* such as non-zero input validation, general boolean expression validation, access validation.
*
* @notice Throws pre-defined errors instead of string error messages to reduce gas costs.
*
* @notice Since the library handles only very common errors, concrete smart contracts may
* also introduce their own error types and handling.
*
* @author Basil Gorin
*/
library ErrorHandler {
/**
* @notice Thrown on zero input at index specified in a function specified.
*
* @param fnSelector function selector, defines a function where error was thrown
* @param paramIndex function parameter index which caused an error thrown
*/
error ZeroInput(bytes4 fnSelector, uint8 paramIndex);
/**
* @notice Thrown on invalid input at index specified in a function specified.
*
* @param fnSelector function selector, defines a function where error was thrown
* @param paramIndex function parameter index which caused an error thrown
*/
error InvalidInput(bytes4 fnSelector, uint8 paramIndex);
/**
* @notice Thrown on invalid state in a function specified.
*
* @param fnSelector function selector, defines a function where error was thrown
* @param errorCode unique error code determining the exact place in code where error was thrown
*/
error InvalidState(bytes4 fnSelector, uint256 errorCode);
/**
* @notice Thrown on invalid access to a function specified.
*
* @param fnSelector function selector, defines a function where error was thrown
* @param addr an address which access was denied, usually transaction sender
*/
error AccessDenied(bytes4 fnSelector, address addr);
/**
* @notice Verifies an input is set (non-zero).
*
* @param fnSelector function selector, defines a function which called the verification
* @param value a value to check if it's set (non-zero)
* @param paramIndex function parameter index which is verified
*/
function verifyNonZeroInput(
bytes4 fnSelector,
uint256 value,
uint8 paramIndex
) internal pure {
if (value == 0) {
revert ZeroInput(fnSelector, paramIndex);
}
}
/**
* @notice Verifies an input is correct.
*
* @param fnSelector function selector, defines a function which called the verification
* @param expr a boolean expression used to verify the input
* @param paramIndex function parameter index which is verified
*/
function verifyInput(
bytes4 fnSelector,
bool expr,
uint8 paramIndex
) internal pure {
if (!expr) {
revert InvalidInput(fnSelector, paramIndex);
}
}
/**
* @notice Verifies smart contract state is correct.
*
* @param fnSelector function selector, defines a function which called the verification
* @param expr a boolean expression used to verify the contract state
* @param errorCode unique error code determining the exact place in code which is verified
*/
function verifyState(
bytes4 fnSelector,
bool expr,
uint256 errorCode
) internal pure {
if (!expr) {
revert InvalidState(fnSelector, errorCode);
}
}
/**
* @notice Verifies an access to the function.
*
* @param fnSelector function selector, defines a function which called the verification
* @param expr a boolean expression used to verify the access
*/
function verifyAccess(bytes4 fnSelector, bool expr) internal view {
if (!expr) {
revert AccessDenied(fnSelector, msg.sender);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
/**
* @dev Stake library used by ILV pool and Sushi LP Pool.
*
* @dev Responsible to manage weight calculation and store important constants
* related to stake period, base weight and multipliers utilized.
*/
library Stake {
struct Data {
/// @dev token amount staked
uint120 value;
/// @dev locking period - from
uint64 lockedFrom;
/// @dev locking period - until
uint64 lockedUntil;
/// @dev indicates if the stake was created as a yield reward
bool isYield;
}
/**
* @dev Stake weight is proportional to stake value and time locked, precisely
* "stake value wei multiplied by (fraction of the year locked plus one)".
* @dev To avoid significant precision loss due to multiplication by "fraction of the year" [0, 1],
* weight is stored multiplied by 1e6 constant, as an integer.
* @dev Corner case 1: if time locked is zero, weight is stake value multiplied by 1e6 + base weight
* @dev Corner case 2: if time locked is two years, division of
(lockedUntil - lockedFrom) / MAX_STAKE_PERIOD is 1e6, and
* weight is a stake value multiplied by 2 * 1e6.
*/
uint256 internal constant WEIGHT_MULTIPLIER = 1e6;
/**
* @dev Minimum weight value, if result of multiplication using WEIGHT_MULTIPLIER
* is 0 (e.g stake flexible), then BASE_WEIGHT is used.
*/
uint256 internal constant BASE_WEIGHT = 1e6;
/**
* @dev Minimum period that someone can lock a stake for.
*/
uint256 internal constant MIN_STAKE_PERIOD = 30 days;
/**
* @dev Maximum period that someone can lock a stake for.
*/
uint256 internal constant MAX_STAKE_PERIOD = 365 days;
/**
* @dev Rewards per weight are stored multiplied by 1e20 as uint.
*/
uint256 internal constant REWARD_PER_WEIGHT_MULTIPLIER = 1e20;
/**
* @dev When we know beforehand that staking is done for yield instead of
* executing `weight()` function we use the following constant.
*/
uint256 internal constant YIELD_STAKE_WEIGHT_MULTIPLIER = 2 * 1e6;
function weight(Data storage _self) internal view returns (uint256) {
return
uint256(
(((_self.lockedUntil - _self.lockedFrom) * WEIGHT_MULTIPLIER) / MAX_STAKE_PERIOD + BASE_WEIGHT) *
_self.value
);
}
/**
* @dev Converts stake weight (not to be mixed with the pool weight) to
* ILV reward value, applying the 10^12 division on weight
*
* @param _weight stake weight
* @param _rewardPerWeight ILV reward per weight
* @param _rewardPerWeightPaid last reward per weight value used for user earnings
* @return reward value normalized to 10^12
*/
function earned(
uint256 _weight,
uint256 _rewardPerWeight,
uint256 _rewardPerWeightPaid
) internal pure returns (uint256) {
// apply the formula and return
return (_weight * (_rewardPerWeight - _rewardPerWeightPaid)) / REWARD_PER_WEIGHT_MULTIPLIER;
}
/**
* @dev Converts reward ILV value to stake weight (not to be mixed with the pool weight),
* applying the 10^12 multiplication on the reward.
* - OR -
* @dev Converts reward ILV value to reward/weight if stake weight is supplied as second
* function parameter instead of reward/weight.
*
* @param _reward yield reward
* @param _globalWeight total weight in the pool
* @return reward per weight value
*/
function getRewardPerWeight(uint256 _reward, uint256 _globalWeight) internal pure returns (uint256) {
// apply the reverse formula and return
return (_reward * REWARD_PER_WEIGHT_MULTIPLIER) / _globalWeight;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import { ICorePool } from "./ICorePool.sol";
interface IFactory {
function owner() external view returns (address);
function ilvPerSecond() external view returns (uint192);
function totalWeight() external view returns (uint32);
function secondsPerUpdate() external view returns (uint32);
function endTime() external view returns (uint32);
function lastRatioUpdate() external view returns (uint32);
function pools(address _poolToken) external view returns (ICorePool);
function poolExists(address _poolAddress) external view returns (bool);
function getPoolAddress(address poolToken) external view returns (address);
function getPoolData(address _poolToken)
external
view
returns (
address,
address,
uint32,
bool
);
function shouldUpdateRatio() external view returns (bool);
function registerPool(ICorePool pool) external;
function updateILVPerSecond() external;
function mintYieldTo(
address _to,
uint256 _value,
bool _useSILV
) external;
function changePoolWeight(address pool, uint32 weight) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import { Stake } from "../libraries/Stake.sol";
interface ICorePool {
function users(address _user)
external
view
returns (
uint128,
uint128,
uint128,
uint248,
uint8,
uint256,
uint256
);
function poolToken() external view returns (address);
function isFlashPool() external view returns (bool);
function weight() external view returns (uint32);
function lastYieldDistribution() external view returns (uint64);
function yieldRewardsPerWeight() external view returns (uint256);
function globalWeight() external view returns (uint256);
function pendingRewards(address _user) external view returns (uint256, uint256);
function poolTokenReserve() external view returns (uint256);
function balanceOf(address _user) external view returns (uint256);
function getTotalReserves() external view returns (uint256);
function getStake(address _user, uint256 _stakeId) external view returns (Stake.Data memory);
function getStakesLength(address _user) external view returns (uint256);
function sync() external;
function setWeight(uint32 _weight) external;
function receiveVaultRewards(uint256 value) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
interface ICorePoolV1 {
struct V1Stake {
// @dev token amount staked
uint256 tokenAmount;
// @dev stake weight
uint256 weight;
// @dev locking period - from
uint64 lockedFrom;
// @dev locking period - until
uint64 lockedUntil;
// @dev indicates if the stake was created as a yield reward
bool isYield;
}
struct V1User {
// @dev Total staked amount
uint256 tokenAmount;
// @dev Total weight
uint256 totalWeight;
// @dev Auxiliary variable for yield calculation
uint256 subYieldRewards;
// @dev Auxiliary variable for vault rewards calculation
uint256 subVaultRewards;
// @dev An array of holder's deposits
V1Stake[] deposits;
}
function users(address _who)
external
view
returns (
uint256,
uint256,
uint256,
uint256
);
function getDeposit(address _from, uint256 _stakeId)
external
view
returns (
uint256,
uint256,
uint64,
uint64,
bool
);
function poolToken() external view returns (address);
function usersLockingWeight() external view returns (uint256);
function poolTokenReserve() external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { V2Migrator } from "./base/V2Migrator.sol";
import { CorePool } from "./base/CorePool.sol";
import { ErrorHandler } from "./libraries/ErrorHandler.sol";
import { ICorePoolV1 } from "./interfaces/ICorePoolV1.sol";
/**
* @title The Sushi LP Pool.
*
* @dev Extends all functionality from V2Migrator contract, there isn't a lot of
* additions compared to ILV pool. Sushi LP pool basically needs to be able
* to be called by ILV pool in batch calls where we claim rewards from multiple
* pools.
*/
contract SushiLPPool is Initializable, V2Migrator {
using ErrorHandler for bytes4;
/// @dev Calls __V2Migrator_init().
function initialize(
address ilv_,
address silv_,
address _poolToken,
address _factory,
uint64 _initTime,
uint32 _weight,
address _corePoolV1,
uint256 v1StakeMaxPeriod_
) external initializer {
__V2Migrator_init(ilv_, silv_, _poolToken, _corePoolV1, _factory, _initTime, _weight, v1StakeMaxPeriod_);
}
/// @inheritdoc CorePool
function getTotalReserves() external view virtual override returns (uint256 totalReserves) {
totalReserves = poolTokenReserve + ICorePoolV1(corePoolV1).poolTokenReserve();
}
/**
* @notice This function can be called only by ILV core pool.
*
* @dev Uses ILV pool as a router by receiving the _staker address and executing
* the internal `_claimYieldRewards()`.
* @dev Its usage allows claiming multiple pool contracts in one transaction.
*
* @param _staker user address
* @param _useSILV whether it should claim pendingYield as ILV or sILV
*/
function claimYieldRewardsFromRouter(address _staker, bool _useSILV) external virtual {
// checks if contract is paused
_requireNotPaused();
// checks if caller is the ILV pool
_requirePoolIsValid();
// calls internal _claimYieldRewards function (in CorePool.sol)
_claimYieldRewards(_staker, _useSILV);
}
/**
* @notice This function can be called only by ILV core pool.
*
* @dev Uses ILV pool as a router by receiving the _staker address and executing
* the internal `_claimVaultRewards()`.
* @dev Its usage allows claiming multiple pool contracts in one transaction.
*
* @param _staker user address
*/
function claimVaultRewardsFromRouter(address _staker) external virtual {
// checks if contract is paused
_requireNotPaused();
// checks if caller is the ILV pool
_requirePoolIsValid();
// calls internal _claimVaultRewards function (in CorePool.sol)
_claimVaultRewards(_staker);
}
/**
* @dev Checks if caller is ILV pool.
* @dev We are using an internal function instead of a modifier in order to
* reduce the contract's bytecode size.
*/
function _requirePoolIsValid() internal view virtual {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("_requirePoolIsValid()"))`
bytes4 fnSelector = 0x250f303f;
// checks if pool is the ILV pool
bool poolIsValid = address(_factory.pools(_ilv)) == msg.sender;
fnSelector.verifyState(poolIsValid, 0);
}
/**
* @dev Empty reserved space in storage. The size of the __gap array is calculated so that
* the amount of storage used by a contract always adds up to the 50.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*
* _Available since v4.1._
*/
abstract contract UUPSUpgradeable is Initializable, ERC1967UpgradeUpgradeable {
function __UUPSUpgradeable_init() internal initializer {
__ERC1967Upgrade_init_unchained();
__UUPSUpgradeable_init_unchained();
}
function __UUPSUpgradeable_init_unchained() internal initializer {
}
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
address private immutable __self = address(this);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
require(address(this) != __self, "Function must be called through delegatecall");
require(_getImplementation() == __self, "Function must be called through active proxy");
_;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*/
function upgradeTo(address newImplementation) external virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallSecure(newImplementation, new bytes(0), false);
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallSecure(newImplementation, data, true);
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeTo} and {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal override onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being 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 percentage 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.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal initializer {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal initializer {
_status = _NOT_ENTERED;
}
/**
* @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(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal initializer {
__Context_init_unchained();
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal initializer {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
/// @title Function for getting block timestamp.
/// @dev Base contract that is overridden for tests.
abstract contract Timestamp {
/**
* @dev Testing time-dependent functionality is difficult and the best way of
* doing it is to override time in helper test smart contracts.
*
* @return `block.timestamp` in mainnet, custom values in testnets (if overridden).
*/
function _now256() internal view virtual returns (uint256) {
// return current block timestamp
return block.timestamp;
}
/**
* @dev Empty reserved space in storage. The size of the __gap array is calculated so that
* the amount of storage used by a contract always adds up to the 50.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { FactoryControlled } from "./FactoryControlled.sol";
import { ErrorHandler } from "../libraries/ErrorHandler.sol";
abstract contract VaultRecipient is Initializable, FactoryControlled {
using ErrorHandler for bytes4;
/// @dev Link to deployed IlluviumVault instance.
address internal _vault;
/// @dev Used to calculate vault rewards.
/// @dev This value is different from "reward per token" used in locked pool.
/// @dev Note: stakes are different in duration and "weight" reflects that,
uint256 public vaultRewardsPerWeight;
/**
* @dev Fired in `setVault()`.
*
* @param by an address which executed the function, always a factory owner
* @param previousVault previous vault contract address
* @param newVault new vault address
*/
event LogSetVault(address indexed by, address previousVault, address newVault);
/**
* @dev Executed only by the factory owner to Set the vault.
*
* @param vault_ an address of deployed IlluviumVault instance
*/
function setVault(address vault_) external virtual {
// we're using selector to simplify input and state validation
bytes4 fnSelector = this.setVault.selector;
// verify function is executed by the factory owner
fnSelector.verifyState(_factory.owner() == msg.sender, 0);
// verify input is set
fnSelector.verifyInput(vault_ != address(0), 0);
// saves current vault to memory
address previousVault = vault_;
// update vault address
_vault = vault_;
// emit an event
emit LogSetVault(msg.sender, previousVault, _vault);
}
/// @dev Utility function to check if caller is the Vault contract
function _requireIsVault() internal view virtual {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("_requireIsVault()"))`
bytes4 fnSelector = 0xeeea774b;
// checks if caller is the same stored vault address
fnSelector.verifyAccess(msg.sender == _vault);
}
/**
* @dev Empty reserved space in storage. The size of the __gap array is calculated so that
* the amount of storage used by a contract always adds up to the 50.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[48] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import { ICorePool } from "./ICorePool.sol";
interface IILVPool is ICorePool {
function stakeAsPool(address _staker, uint256 _value) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "../beacon/IBeaconUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol";
import "../utils/Initializable.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*
* @custom:oz-upgrades-unsafe-allow delegatecall
*/
abstract contract ERC1967UpgradeUpgradeable is Initializable {
function __ERC1967Upgrade_init() internal initializer {
__ERC1967Upgrade_init_unchained();
}
function __ERC1967Upgrade_init_unchained() internal initializer {
}
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
_upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
_functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallSecure(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
address oldImplementation = _getImplementation();
// Initial upgrade and setup call
_setImplementation(newImplementation);
if (data.length > 0 || forceCall) {
_functionDelegateCall(newImplementation, data);
}
// Perform rollback test if not already in progress
StorageSlotUpgradeable.BooleanSlot storage rollbackTesting = StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT);
if (!rollbackTesting.value) {
// Trigger rollback using upgradeTo from the new implementation
rollbackTesting.value = true;
_functionDelegateCall(
newImplementation,
abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
);
rollbackTesting.value = false;
// Check rollback was effective
require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
// Finally reset to the new implementation and log the upgrade
_upgradeTo(newImplementation);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Returns the current admin.
*/
function _getAdmin() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Emitted when the beacon is upgraded.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
require(
AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(
address newBeacon,
bytes memory data,
bool forceCall
) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
_functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
}
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeaconUpgradeable {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/
library StorageSlotUpgradeable {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly {
r.slot := slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal initializer {
__Context_init_unchained();
}
function __Context_init_unchained() internal initializer {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { IFactory } from "../interfaces/IFactory.sol";
import { ErrorHandler } from "../libraries/ErrorHandler.sol";
/**
* @title FactoryControlled
*
* @dev Abstract smart contract responsible to hold IFactory factory address.
* @dev Stores PoolFactory address on initialization.
*
*/
abstract contract FactoryControlled is Initializable {
using ErrorHandler for bytes4;
/// @dev Link to the pool factory IlluviumPoolFactory instance.
IFactory internal _factory;
/// @dev Attachs PoolFactory address to the FactoryControlled contract.
function __FactoryControlled_init(address factory_) internal initializer {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("__FactoryControlled_init(address)"))`
bytes4 fnSelector = 0xbb6c0dbf;
fnSelector.verifyNonZeroInput(uint160(factory_), 0);
_factory = IFactory(factory_);
}
/// @dev checks if caller is factory admin (eDAO multisig address).
function _requireIsFactoryController() internal view virtual {
// we're using selector to simplify input and state validation
// internal function simulated selector is `bytes4(keccak256("_requireIsFactoryController()"))`
bytes4 fnSelector = 0x39e71deb;
fnSelector.verifyAccess(msg.sender == _factory.owner());
}
/**
* @dev Empty reserved space in storage. The size of the __gap array is calculated so that
* the amount of storage used by a contract always adds up to the 50.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}{
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"internalType":"address","name":"addr","type":"address"}],"name":"AccessDenied","type":"error"},{"inputs":[{"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"internalType":"uint8","name":"paramIndex","type":"uint8"}],"name":"InvalidInput","type":"error"},{"inputs":[{"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"InvalidState","type":"error"},{"inputs":[{"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"internalType":"uint8","name":"paramIndex","type":"uint8"}],"name":"ZeroInput","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"LogClaimVaultRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"bool","name":"sILV","type":"bool"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"LogClaimYieldRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalV1WeightAdded","type":"uint256"}],"name":"LogMigrateLockedStakes","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"pendingRewardsMigrated","type":"uint256"},{"indexed":false,"internalType":"bool","name":"useSILV","type":"bool"}],"name":"LogMigratePendingRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"yieldWeightMigrated","type":"uint256"}],"name":"LogMigrateYieldWeight","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint248","name":"previousTotalWeight","type":"uint248"},{"indexed":false,"internalType":"uint248","name":"newTotalWeight","type":"uint248"},{"indexed":false,"internalType":"uint128","name":"previousYield","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"newYield","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"previousRevDis","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"newRevDis","type":"uint128"}],"name":"LogMoveFundsFromWallet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"LogReceiveVaultRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"address","name":"previousVault","type":"address"},{"indexed":false,"internalType":"address","name":"newVault","type":"address"}],"name":"LogSetVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"lockUntil","type":"uint64"}],"name":"LogStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"yieldRewardsPerWeight","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"lastYieldDistribution","type":"uint64"}],"name":"LogSync","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isYield","type":"bool"}],"name":"LogUnstakeLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalValue","type":"uint256"},{"indexed":false,"internalType":"bool","name":"unstakingYield","type":"bool"}],"name":"LogUnstakeLockedMultiple","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"yieldValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"revDisValue","type":"uint256"}],"name":"LogUpdateRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"LogV1YieldMintedMultiple","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"}],"name":"blacklistUsers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_useSILV","type":"bool"}],"name":"claimAllRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimVaultRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pools","type":"address[]"}],"name":"claimVaultRewardsMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_useSILV","type":"bool"}],"name":"claimYieldRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pools","type":"address[]"},{"internalType":"bool[]","name":"_useSILV","type":"bool[]"}],"name":"claimYieldRewardsMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint248","name":"_yieldWeight","type":"uint248"},{"internalType":"uint256","name":"_pendingV1Rewards","type":"uint256"},{"internalType":"bool","name":"_useSILV","type":"bool"},{"internalType":"uint256[]","name":"_stakeIds","type":"uint256[]"}],"name":"executeMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_stakeId","type":"uint256"}],"name":"getStake","outputs":[{"components":[{"internalType":"uint120","name":"value","type":"uint120"},{"internalType":"uint64","name":"lockedFrom","type":"uint64"},{"internalType":"uint64","name":"lockedUntil","type":"uint64"},{"internalType":"bool","name":"isYield","type":"bool"}],"internalType":"struct Stake.Data","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getStakesLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalReserves","outputs":[{"internalType":"uint256","name":"totalReserves","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_position","type":"uint256"}],"name":"getV1StakeId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_desiredId","type":"uint256"}],"name":"getV1StakePosition","outputs":[{"internalType":"uint256","name":"position","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"hasMigratedYield","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ilv_","type":"address"},{"internalType":"address","name":"silv_","type":"address"},{"internalType":"address","name":"_poolToken","type":"address"},{"internalType":"address","name":"factory_","type":"address"},{"internalType":"uint64","name":"_initTime","type":"uint64"},{"internalType":"uint32","name":"_weight","type":"uint32"},{"internalType":"address","name":"_corePoolV1","type":"address"},{"internalType":"uint256","name":"v1StakeMaxPeriod_","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFlashPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastYieldDistribution","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_stakeIds","type":"uint256[]"}],"name":"migrateLockedStakes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_stakeIds","type":"uint256[]"}],"name":"mintV1YieldMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"moveFundsFromWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_shouldPause","type":"bool"}],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"pendingRewards","outputs":[{"internalType":"uint256","name":"pendingYield","type":"uint256"},{"internalType":"uint256","name":"pendingRevDis","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolTokenReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"receiveVaultRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_v1GlobalWeight","type":"uint256"}],"name":"setV1GlobalWeight","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_v1PoolTokenReserve","type":"uint256"}],"name":"setV1PoolTokenReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault_","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_weight","type":"uint32"}],"name":"setWeight","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint64","name":"_lockDuration","type":"uint64"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"stakeAsPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stakeId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"stakeId","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct CorePool.UnstakeParameter[]","name":"_stakes","type":"tuple[]"},{"internalType":"bool","name":"_unstakingYield","type":"bool"}],"name":"unstakeMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"users","outputs":[{"internalType":"uint128","name":"pendingYield","type":"uint128"},{"internalType":"uint128","name":"pendingRevDis","type":"uint128"},{"internalType":"uint248","name":"totalWeight","type":"uint248"},{"internalType":"uint8","name":"v1IdsLength","type":"uint8"},{"internalType":"uint256","name":"yieldRewardsPerWeightPaid","type":"uint256"},{"internalType":"uint256","name":"vaultRewardsPerWeightPaid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"v1GlobalWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"v1PoolTokenReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"v1StakesWeights","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"v1YieldMinted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultRewardsPerWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weight","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldRewardsPerWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a06040523060601b60805234801561001757600080fd5b5060805160601c615a556200004c60003960008181611416015281816114560152818161191501526119550152615a556000f3fe6080604052600436106102ad5760003560e01c806370a0823111610175578063afe0b716116100dc578063dc60e2cb11610095578063ef0c79241161006f578063ef0c7924146109a2578063fa213bd6146109ea578063fe575a8714610a01578063fff6cae914610a3257600080fd5b8063dc60e2cb1461093b578063e5f83e6c14610974578063ed91b7de1461098b57600080fd5b8063afe0b7161461081f578063b1aca5041461083f578063bb3d676a1461085f578063cbdf382c1461087f578063ce111541146108b8578063cfd47663146108cf57600080fd5b80639e2c8a5b1161012e5780639e2c8a5b1461069e578063a1aab33f146106be578063a4292812146106f8578063a87430ba14610718578063ac89352a146107cc578063ae9f245a1461080857600080fd5b806370a08231146105c457806374bd128f146105e45780637cb64759146106045780638b323d1114610624578063909767d914610644578063952e68cf1461067e57600080fd5b80633659cfe611610219578063583c1aca116101d2578063583c1aca1461050b57806358c1900f1461052b5780635c7f74bb1461054b5780635c975abb1461056b5780636817031b146105845780636955cf92146105a457600080fd5b80633659cfe6146104585780633b350f90146104785780634087aeb71461049857806344cc892d146104b85780634568036e146104d85780634f1ef286146104f857600080fd5b80631da10d911161026b5780631da10d9114610372578063242693d31461039757806329eb5f2c146103ac5780632eb4a7ab146103ec5780633021a5601461040357806331d7a2621461042357600080fd5b806251ccba146102b257806302329a29146102d457806305472358146102f4578063114952be1461031d5780631965a5991461033d5780631bfe80b814610352575b600080fd5b3480156102be57600080fd5b506102d26102cd3660046154f0565b610a47565b005b3480156102e057600080fd5b506102d26102ef3660046154f0565b610a5c565b34801561030057600080fd5b5061030a60985481565b6040519081526020015b60405180910390f35b34801561032957600080fd5b506102d261033836600461518e565b610a7a565b34801561034957600080fd5b506102d2610b07565b34801561035e57600080fd5b506102d261036d366004615470565b610b1a565b34801561037e57600080fd5b50610387600081565b6040519015158152602001610314565b3480156103a357600080fd5b5061030a610ee8565b3480156103b857600080fd5b50610196546103d490600160c01b90046001600160401b031681565b6040516001600160401b039091168152602001610314565b3480156103f857600080fd5b5061030a6101f45481565b34801561040f57600080fd5b506102d261041e366004615528565b610f01565b34801561042f57600080fd5b5061044361043e366004615156565b610fd9565b60408051928352602083019190915201610314565b34801561046457600080fd5b506102d2610473366004615156565b61140b565b34801561048457600080fd5b506102d2610493366004615156565b6114d1565b3480156104a457600080fd5b506102d26104b336600461564c565b611585565b3480156104c457600080fd5b506102d26104d33660046152eb565b6115d4565b3480156104e457600080fd5b506103876104f3366004615528565b6118e4565b6102d261050636600461522d565b61190a565b34801561051757600080fd5b506102d2610526366004615316565b6119c4565b34801561053757600080fd5b506102d26105463660046153bd565b611d0c565b34801561055757600080fd5b506102d2610566366004615355565b611d64565b34801561057757600080fd5b5061012d5460ff16610387565b34801561059057600080fd5b506102d261059f366004615156565b611fec565b3480156105b057600080fd5b506102d26105bf366004615316565b61211a565b3480156105d057600080fd5b5061030a6105df366004615156565b61214c565b3480156105f057600080fd5b506102d26105ff366004615528565b6121d0565b34801561061057600080fd5b506102d261061f366004615528565b6121de565b34801561063057600080fd5b506102d261063f366004615316565b6121ec565b34801561065057600080fd5b5061030a61065f366004615156565b6001600160a01b03166000908152610191602052604090206004015490565b34801561068a57600080fd5b506102d261069936600461561d565b6123d2565b3480156106aa57600080fd5b506102d26106b9366004615567565b612701565b3480156106ca57600080fd5b50610196546106e390600160a01b900463ffffffff1681565b60405163ffffffff9091168152602001610314565b34801561070457600080fd5b506102d26107133660046154f0565b6129dc565b34801561072457600080fd5b50610783610733366004615156565b6101916020526000908152604090208054600182015460028301546003909301546001600160801b0380841694600160801b90940416926001600160f81b03831692600160f81b900460ff169186565b604080516001600160801b0397881681529690951660208701526001600160f81b039093169385019390935260ff166060840152608083019190915260a082015260c001610314565b3480156107d857600080fd5b506103876107e73660046152eb565b6101f660209081526000928352604080842090915290825290205460ff1681565b34801561081457600080fd5b5061030a6101985481565b34801561082b57600080fd5b506102d261083a366004615528565b6129ed565b34801561084b57600080fd5b5061030a61085a3660046152eb565b6129fb565b34801561086b57600080fd5b506102d261087a366004615316565b612a65565b34801561088b57600080fd5b50610196546108a0906001600160a01b031681565b6040516001600160a01b039091168152602001610314565b3480156108c457600080fd5b5061030a61019a5481565b3480156108db57600080fd5b506108ef6108ea3660046152eb565b612b57565b6040805182516001600160781b031681526020808401516001600160401b0390811691830191909152838301511691810191909152606091820151151591810191909152608001610314565b34801561094757600080fd5b5061030a6109563660046152eb565b61019260209081526000928352604080842090915290825290205481565b34801561098057600080fd5b5061030a6101f75481565b34801561099757600080fd5b5061030a6101995481565b3480156109ae57600080fd5b5061030a6109bd3660046152eb565b6001600160a01b039190911660009081526101916020908152604080832093835260059093019052205490565b3480156109f657600080fd5b5061030a6101975481565b348015610a0d57600080fd5b50610387610a1c366004615156565b6101c26020526000908152604090205460ff1681565b348015610a3e57600080fd5b506102d2612c19565b610a4f612c29565b610a593382612c55565b50565b610a64612ff2565b8015610a7257610a59613098565b610a59613132565b600054610100900460ff1680610a93575060005460ff16155b610ab85760405162461bcd60e51b8152600401610aaf9061578e565b60405180910390fd5b600054610100900460ff16158015610ada576000805461ffff19166101011790555b610aea898989868a8a8a896131ae565b8015610afc576000805461ff00191690555b505050505050505050565b610b0f612c29565b610b1833613236565b565b610b22612c29565b63037fd01760e31b610b36818460006132ed565b3360008181526101916020526040812091610b5090613320565b9050610b5c3382613497565b60008060005b87811015610d9b576000808a8a84818110610b8d57634e487b7160e01b600052603260045260246000fd5b905060400201600001358b8b85818110610bb757634e487b7160e01b600052603260045260246000fd5b90506040020160200135915091506000876004018381548110610bea57634e487b7160e01b600052603260045260246000fd5b60009182526020909120018054909150610c2d90600160b81b90046001600160401b03164211610c1b866003615883565b6001600160e01b03198c16919061360a565b610c436001600160e01b03198a168360016132ed565b80546001600160781b03811690600160f81b900460ff16610c8c8c151582151514610c6f886003615883565b610c7a906001615829565b6001600160e01b03198e16919061360a565b610cb26001600160781b038316851115610ca7886003615883565b610c7a906002615829565b6000610cbd8461363b565b90506000610cd4866001600160781b0386166158ea565b610d0b578b6004018781548110610cfb57634e487b7160e01b600052603260045260246000fd5b6000918252602082200155610d5f565b610d14866136ac565b85548690600090610d2f9084906001600160781b03166158a2565b92506101000a8154816001600160781b0302191690836001600160781b03160217905550610d5c8561363b565b90505b610d6981836158ea565b610d73908b615829565b9950610d7f868a615829565b9850505050505050508080610d9390615970565b915050610b62565b50610da5826136d2565b600185018054600090610dc29084906001600160f81b03166158ca565b92506101000a8154816001600160f81b0302191690836001600160f81b03160217905550816101986000828254610df991906158ea565b925050819055508061019a6000828254610e1391906158ea565b90915550508515610e8a576065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca90610e5390339085906000906004016156a0565b600060405180830381600087803b158015610e6d57600080fd5b505af1158015610e81573d6000803e3d6000fd5b50505050610ea2565b61019654610ea2906001600160a01b031633836136f1565b60408051828152871515602082015233917f30d438cf38db3f29630029343ab01e801276913697f489030c0613ddc4aadaaf910160405180910390a25050505050505050565b60006101f75461019a54610efc9190615829565b905090565b610f09613754565b610f11612c29565b610f19613b3f565b6301810d2b60e51b81610f2a575050565b610f556000610198541180610f425750600061019954115b6001600160e01b0319831690600061360a565b610f716101995461019854610f6a9190615829565b8390613b61565b60986000828254610f829190615829565b909155505061019454610fa0906001600160a01b0316333085613b88565b60405182815233907f55fd0bec59e0b2fdf9406c9890f568c6ec6f92c752012ef7e3ebaee44d4d6cad9060200160405180910390a25050565b600080610ff76318ebd13160e11b6001600160a01b038516836132ed565b610196546001600160a01b03841660009081526101916020526040812060018101549192600160c01b90046001600160401b031691600160f81b810460ff16906001600160f81b031684821561114d5760005b8381101561114b576000818152600586016020526040808220546101955491516313935a8360e11b81526001600160a01b038e81166004830152602482018390529193929190911690632726b5069060440160a06040518083038186803b1580156110b457600080fd5b505afa1580156110c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ec91906155bd565b5050506001600160a01b038e166000908152610192602090815260408083208784529091529020549092509050808211156111275780611129565b815b6111339086615829565b9450505050808061114390615970565b91505061104a565b505b844211801561115e57506101985415155b1561138b57606554604080516318cbe5db60e11b815290516000926001600160a01b031691633197cbb6916004808301926020929190829003018186803b1580156111a857600080fd5b505afa1580156111bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e09190615668565b63ffffffff1690506000814211611200576111fb87426158ea565b61120a565b61120a87836158ea565b90506000606560009054906101000a90046001600160a01b03166001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b15801561125c57600080fd5b505afa158015611270573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112949190615668565b63ffffffff16606560009054906101000a90046001600160a01b03166001600160a01b0316634251342f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156112e857600080fd5b505afa1580156112fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113209190615540565b610196546001600160c01b03919091169061134890600160a01b900463ffffffff1685615883565b6113529190615883565b61135c9190615863565b9050610197546113776101995461019854610f6a9190615829565b6113819190615829565b9850505050611392565b6101975495505b835460028501546001600160801b03909116906113bc9088906113b58587615829565b9190613bc0565b6113c69190615829565b84546098546003870154929a50600160801b9091046001600160801b0316916113f491906113b58587615829565b6113fe9190615829565b9650505050505050915091565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614156114545760405162461bcd60e51b8152600401610aaf906156f6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611486613bf2565b6001600160a01b0316146114ac5760405162461bcd60e51b8152600401610aaf90615742565b6114b581613c20565b60408051600080825260208201909252610a5991839190613c28565b6101955460405163543a185d60e11b81523360048201526303b350f960e41b916000916001600160a01b039091169063a87430ba9060240160806040518083038186803b15801561152157600080fd5b505afa158015611535573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115599190615588565b509092506115779150506001600160e01b031983168215600061360a565b61158083613d73565b505050565b61158d613754565b6065546115ad90634087aeb760e01b906001600160a01b03163314613f5c565b610196805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b600260c95414156116275760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610aaf565b600260c955611634612c29565b606554604051631e1c6a0760e01b81523360048201526116bf916001600160a01b031690631e1c6a079060240160206040518083038186803b15801561167957600080fd5b505afa15801561168d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b1919061550c565b6344cc892d60e01b90613f5c565b6001600160a01b038216600090815261019160205260408120906116e284613320565b90506116ee8482613497565b60006116fd621e848085615883565b905060006040518060800160405280611715876136ac565b6001600160781b0316815260200161173142613f8c565b613f8c565b6001600160401b031681526020016117536301e13380425b61172c9190615829565b6001600160401b0316815260016020909101529050611771826136d2565b60018501805460009061178e9084906001600160f81b0316615807565b82546101009290920a6001600160f81b038181021990931691831602179091556004860180546001810182556000918252602080832086519201805491870151604088015160608901516001600160781b039095166001600160b81b031990941693909317600160781b6001600160401b0392831602176001600160b81b0316600160b81b919093160290941617600160f81b91151591909102179091556101988054859350909190611842908490615829565b925050819055508461019a600082825461185c9190615829565b909155505060048401546001600160a01b0387169033907f47ee2438657f63a52980ec1ed6e4424262f81f621308d91694c410db5bfa401b906118a1906001906158ea565b886118b06301e1338042611749565b6040805193845260208401929092526001600160401b03169082015260600160405180910390a35050600160c95550505050565b600881901c60009081526101f56020526040812054600160ff84161b1615155b92915050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614156119535760405162461bcd60e51b8152600401610aaf906156f6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611985613bf2565b6001600160a01b0316146119ab5760405162461bcd60e51b8152600401610aaf90615742565b6119b482613c20565b6119c082826001613c28565b5050565b632c1e0d6560e11b6119d533613fab565b6119dd612c29565b33600081815261019160205260408120918190819081906119fd90613320565b9050611a093382613497565b905060005b86811015611bdf576000888883818110611a3857634e487b7160e01b600052603260045260246000fd5b610195546040516313935a8360e11b8152336004820152602090920293909301356024820181905293506000928392508291829182916001600160a01b0390911690632726b5069060440160a06040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ad391906155bd565b94509450945094509450611b0081886003611aee9190615883565b6001600160e01b03198f16919061360a565b611b256001600160401b0383164211611b1a896004615883565b611aee906001615829565b3360009081526101f660209081526040808320898452909152902054611b5f9060ff1615611b54896005615883565b611aee906002615829565b6101c354611b88906001600160401b03851610611b7d896006615883565b611aee906003615829565b3360009081526101f6602090815260408083208984529091529020805460ff19166001179055611bb8858b615829565b9950611bc4848a615829565b98505050505050508080611bd790615970565b915050611a0e565b50611be9826136d2565b600185018054600090611c069084906001600160f81b03166158ca565b92506101000a8154816001600160f81b0302191690836001600160f81b03160217905550816101986000828254611c3d91906158ea565b90915550611c509050621e848083615863565b61019a6000828254611c6291906158ea565b90915550506065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca90611c9c90339087906000906004016156a0565b600060405180830381600087803b158015611cb657600080fd5b505af1158015611cca573d6000803e3d6000fd5b50506040518581523392507f28700c38b1cd61046b9139c648b8f2c6959575cce07dcc3556ddd4906e0a6dac915060200160405180910390a250505050505050565b611d1533613fab565b611d1d612c29565b6000611d2833613320565b9050611d343382613497565b611d3e8383613fdd565b6001600160f81b03861615610afc57610afc898989896001600160f81b031689896142d3565b611d6c612c29565b635c7f74bb60e01b611d828185841460006144d2565b60005b84811015611fe4576000868683818110611daf57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611dc49190615156565b606554604051631e1c6a0760e01b81526001600160a01b038084166004830152929350611e599290911690631e1c6a079060240160206040518083038186803b158015611e1057600080fd5b505afa158015611e24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e48919061550c565b6001600160e01b0319851690613f5c565b61019454604080516332f7ce0b60e21b815290516001600160a01b039283169284169163cbdf382c916004808301926020929190829003018186803b158015611ea157600080fd5b505afa158015611eb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed99190615172565b6001600160a01b03161415611f2b57611f2633868685818110611f0c57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611f2191906154f0565b612c55565b611fd1565b806001600160a01b03166353961e0633878786818110611f5b57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611f7091906154f0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015215156024820152604401600060405180830381600087803b158015611fb857600080fd5b505af1158015611fcc573d6000803e3d6000fd5b505050505b5080611fdc81615970565b915050611d85565b505050505050565b60655460408051638da5cb5b60e01b81529051636817031b60e01b926120989233926001600160a01b0390921691638da5cb5b91600480820192602092909190829003018186803b15801561204057600080fd5b505afa158015612054573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120789190615172565b6001600160e01b03198416916001600160a01b039190911614600061360a565b6120b96001600160e01b031982166001600160a01b038416151560006144d2565b609780546001600160a01b0319166001600160a01b038416908117909155604080518281526020810192909252839133917faa55cb88c5664f8f0bbaf57c2fb02459d3a8dd15bfc1e4490335358804da3234910160405180910390a2505050565b61212333613fab565b61212b612c29565b600061213633613320565b90506121423382613497565b6115808383613fdd565b6001600160a01b038116600090815261019160205260408120815b60048201548110156121c95781600401818154811061219657634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546121b5906001600160781b031684615829565b9250806121c181615970565b915050612167565b5050919050565b6121d8612ff2565b61019955565b6121e6612ff2565b6101f455565b6121f4612c29565b60005b8181101561158057600083838381811061222157634e487b7160e01b600052603260045260246000fd5b90506020020160208101906122369190615156565b606554604051631e1c6a0760e01b81526001600160a01b0380841660048301529293506122c89290911690631e1c6a079060240160206040518083038186803b15801561228257600080fd5b505afa158015612296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ba919061550c565b638b323d1160e01b90613f5c565b61019454604080516332f7ce0b60e21b815290516001600160a01b039283169284169163cbdf382c916004808301926020929190829003018186803b15801561231057600080fd5b505afa158015612324573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123489190615172565b6001600160a01b031614156123655761236033613236565b6123bf565b604051630834ef4560e01b81523360048201526001600160a01b03821690630834ef4590602401600060405180830381600087803b1580156123a657600080fd5b505af11580156123ba573d6000803e3d6000fd5b505050505b50806123ca81615970565b9150506121f7565b600260c95414156124255760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610aaf565b600260c955612432612c29565b63952e68cf60e01b612446818460016132ed565b61248562278d00836001600160401b03161015801561247257506301e13380836001600160401b031611155b6001600160e01b031983169060026144d2565b336000818152610191602052604081209161249f90613320565b90506124ab3382613497565b6000846124b742613f8c565b6124c19190615841565b9050600086620f42406301e13380816124e3426001600160401b0388166158ea565b6124ed9190615883565b6124f79190615863565b6125019190615829565b61250b9190615883565b90506000811161252b57634e487b7160e01b600052600160045260246000fd5b600060405180608001604052806125418a6136ac565b6001600160781b0316815260200161255842613f8c565b6001600160401b0390811682528581166020808401919091526000604093840181905260048a01805460018101825590825290829020855191018054928601519486015160608701511515600160f81b026001600160f81b03918616600160b81b02919091166001600160b81b0396909516600160781b026001600160b81b03199094166001600160781b03909316929092179290921793909316919091179190911790559050612608826136d2565b6001860180546000906126259084906001600160f81b0316615807565b92506101000a8154816001600160f81b0302191690836001600160f81b0316021790555081610198600082825461265c9190615829565b925050819055508761019a60008282546126769190615829565b909155505061019654612694906001600160a01b031633308b613b88565b6004850154339081907f47ee2438657f63a52980ec1ed6e4424262f81f621308d91694c410db5bfa401b906126cb906001906158ea565b60408051918252602082018d90526001600160401b0388169082015260600160405180910390a35050600160c955505050505050565b612709612c29565b639e2c8a5b60e01b61271d818360006132ed565b336000908152610191602052604081206004810180549192918690811061275457634e487b7160e01b600052603260045260246000fd5b6000918252602090912001805490915061278d90600160b81b90046001600160401b0316426001600160e01b031986169110600061360a565b80546001600160781b03811690600160f81b900460ff166127bd6001600160e01b031986168784101560016144d2565b60006127c833613320565b90506127d43382613497565b60006127df8561363b565b905060006127f6896001600160781b0387166158ea565b61282d57866004018a8154811061281d57634e487b7160e01b600052603260045260246000fd5b6000918252602082200155612881565b612836896136ac565b865487906000906128519084906001600160781b03166158a2565b92506101000a8154816001600160781b0302191690836001600160781b0316021790555061287e8661363b565b90505b6001870154819061289c9084906001600160f81b03166158ea565b6128a69190615829565b6001880180546001600160f81b0319166001600160f81b03929092169190911790556101985481906128d99084906158ea565b6128e39190615829565b610198819055508861019a60008282546128fd91906158ea565b90915550508315612974576065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca9061293d9033908d906000906004016156a0565b600060405180830381600087803b15801561295757600080fd5b505af115801561296b573d6000803e3d6000fd5b5050505061298c565b6101965461298c906001600160a01b0316338b6136f1565b604080518b8152602081018b905285151581830152905133917f729ab3b283445a8f221b97237248ecc12a89cafc93156a0c77e1174a01ef1c16919081900360600190a250505050505050505050565b6129e4612c29565b610a4f33613236565b6129f5612ff2565b6101f755565b6001600160a01b038216600090815261019160205260408120815b6001820154600160f81b900460ff16811015612a5f576000818152600583016020526040902054841415612a4d5791506119049050565b80612a5781615970565b915050612a16565b50600080fd5b612a6d612ff2565b635d9eb3b560e11b60005b82811015612b5157612adb6000858584818110612aa557634e487b7160e01b600052603260045260246000fd5b9050602002016020810190612aba9190615156565b6001600160e01b03198516916001600160a01b0391909116141560006144d2565b60016101c26000868685818110612b0257634e487b7160e01b600052603260045260246000fd5b9050602002016020810190612b179190615156565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905580612b4981615970565b915050612a78565b50505050565b6040805160808101825260008082526020808301829052828401829052606083018290526001600160a01b0386168252610191905291909120600401805483908110612bb357634e487b7160e01b600052603260045260246000fd5b60009182526020918290206040805160808101825292909101546001600160781b0381168352600160781b81046001600160401b0390811694840194909452600160b81b810490931690820152600160f81b90910460ff16151560608201529392505050565b612c21612c29565b610b18613754565b63abb87a6f60e01b610a59612c4161012d5460ff1690565b6001600160e01b031983169015600061360a565b6001600160a01b03821660009081526101916020526040812090612c7884613320565b9050612c848482613497565b81546001600160801b031680612c9b575050505050565b82546fffffffffffffffffffffffffffffffff191683558315612d24576065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca90612ced90889085906001906004016156a0565b600060405180830381600087803b158015612d0757600080fd5b505af1158015612d1b573d6000803e3d6000fd5b50505050612fa3565b61019454610196546001600160a01b0390811691161415612eb6576000612d4e621e848083615883565b905060006040518060800160405280612d66856136ac565b6001600160781b03168152602001612d7d42613f8c565b6001600160401b03168152602001612d996301e1338042611749565b6001600160401b039081168252600160209283018190526004890180549182018155600090815283902084519101805493850151604086015160608701511515600160f81b026001600160f81b03918616600160b81b02919091166001600160b81b0392909516600160781b026001600160b81b03199096166001600160781b03909416939093179490941793909316919091171790559050612e3b826136d2565b600186018054600090612e589084906001600160f81b0316615807565b92506101000a8154816001600160f81b0302191690836001600160f81b03160217905550816101986000828254612e8f9190615829565b925050819055508261019a6000828254612ea99190615829565b90915550612fa392505050565b6065546101945460405163091465f760e11b81526001600160a01b0391821660048201526000929190911690631228cbee9060240160206040518083038186803b158015612f0357600080fd5b505afa158015612f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f3b9190615172565b6040516344cc892d60e01b81526001600160a01b03888116600483015260248201859052919250908216906344cc892d90604401600060405180830381600087803b158015612f8957600080fd5b505af1158015612f9d573d6000803e3d6000fd5b50505050505b604080518515158152602081018390526001600160a01b0387169133917f7aa2446843f85ab4372b9a9eddbe072a35cd062fb199eaddea2ad3b8d0396fa2910160405180910390a35050505050565b60655460408051638da5cb5b60e01b815290516339e71deb60e01b92610a59926001600160a01b0390911691638da5cb5b91600480820192602092909190829003018186803b15801561304457600080fd5b505afa158015613058573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307c9190615172565b6001600160e01b03198316906001600160a01b03163314613f5c565b61012d5460ff16156130df5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610aaf565b61012d805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586131153390565b6040516001600160a01b03909116815260200160405180910390a1565b61012d5460ff1661317c5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610aaf565b61012d805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613115565b600054610100900460ff16806131c7575060005460ff16155b6131e35760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015613205576000805461ffff19166101011790555b61321489898989898989614505565b6101c38290558015610afc576000805461ff0019169055505050505050505050565b6001600160a01b0381166000908152610191602052604081209061325983613320565b90506132658382613497565b8154600160801b90046001600160801b0316806132825750505050565b82546001600160801b03168355610194546132a7906001600160a01b031685836136f1565b6040518181526001600160a01b0385169033907fa69db2085ef89718710bae5b78f2032cf155ff99feca17d718e69f8ba375c11e9060200160405180910390a350505050565b8161158057604051633bd8dd9360e21b81526001600160e01b03198416600482015260ff82166024820152604401610aaf565b6001600160a01b0381166000908152610191602052604081206001810154600160f81b900460ff1680156121c95760005b8181101561348f576000818152600584016020526040808220546101955491516313935a8360e11b81526001600160a01b038981166004830152602482018390529193929190911690632726b5069060440160a06040518083038186803b1580156133bb57600080fd5b505afa1580156133cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133f391906155bd565b5050506001600160a01b03891660009081526101926020908152604080832087845290915290205490925090508082111561342e5780613430565b815b61343a9088615829565b9650818111156134795781156134505781613453565b60015b6001600160a01b0389166000908152610192602090815260408083208784529091529020555b505050808061348790615970565b915050613351565b505050919050565b61349f613754565b6001600160a01b03821660009081526101916020526040812060018101549091906134d49084906001600160f81b0316615829565b905060006134f461019754846002015484613bc09092919063ffffffff16565b90506000613513609854856003015485613bc09092919063ffffffff16565b905061351e8261468b565b845485906000906135399084906001600160801b03166157dc565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506135668161468b565b84548590601090613588908490600160801b90046001600160801b03166157dc565b82546001600160801b039182166101009390930a928302919092021990911617905550610197546002850155609854600385015560408051838152602081018390526001600160a01b0388169133917f31eeb6a0d26c29b4c243d704ff4ae6feebcc2e8b123df7ea0bd12083c3083cb8910160405180910390a3505050505050565b8161158057604051631db762fb60e31b81526001600160e01b03198416600482015260248101829052604401610aaf565b80546000906001600160781b03811690620f4240906301e1338090829061367b906001600160401b03600160781b8204811691600160b81b900416615901565b6001600160401b031661368e9190615883565b6136989190615863565b6136a29190615829565b6119049190615883565b6000631e4e4bad60e01b6136cb816001600160781b03851115846144d2565b5090919050565b6000631feb933960e11b6136cb816001600160f81b03851115846144d2565b6040516001600160a01b03831660248201526044810182905261158090849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526146aa565b60655460408051639f1dc9bd60e01b815290516001600160a01b03909216918291639f1dc9bd916004808301926020929190829003018186803b15801561379a57600080fd5b505afa1580156137ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137d2919061550c565b1561382b57806001600160a01b031663a83c76646040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561381257600080fd5b505af1158015613826573d6000803e3d6000fd5b505050505b6000816001600160a01b0316633197cbb66040518163ffffffff1660e01b815260040160206040518083038186803b15801561386657600080fd5b505afa15801561387a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061389e9190615668565b6101965463ffffffff919091169150600160c01b90046001600160401b031681116138c7575050565b61019654600160c01b90046001600160401b031642116138e5575050565b610198541580156138f7575061019954155b156139305761390542613f8c565b61019660186101000a8154816001600160401b0302191690836001600160401b031602179055505050565b600081421161393f5742613941565b815b6101965490915060009061396590600160c01b90046001600160401b0316836158ea565b90506000846001600160a01b0316634251342f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156139a257600080fd5b505afa1580156139b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139da9190615540565b6001600160c01b031690506000856001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b158015613a2057600080fd5b505afa158015613a34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a589190615668565b6101965463ffffffff91821691600160a01b90910416613a788486615883565b613a829190615883565b613a8c9190615863565b9050613aaa6101995461019854613aa39190615829565b8290613b61565b6101976000828254613abc9190615829565b90915550613acb905084613f8c565b61019680546001600160c01b0316600160c01b6001600160401b0393841681029190911791829055610197546040805191825291909204909216602082015233917f33e6f269701b611439c5bd9eae485d1b2f10d29b632a6f0d5688c93c2d77af1f910160405180910390a2505050505050565b60975463eeea774b60e01b90610a599082906001600160a01b03163314613f5c565b600081613b7768056bc75e2d6310000085615883565b613b819190615863565b9392505050565b6040516001600160a01b0380851660248301528316604482015260648101829052612b519085906323b872dd60e01b9060840161371d565b600068056bc75e2d63100000613bd683856158ea565b613be09086615883565b613bea9190615863565b949350505050565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b610a59612ff2565b6000613c32613bf2565b9050613c3d8461477c565b600083511180613c4a5750815b15613c5b57613c598484614821565b505b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143805460ff16613d6c57805460ff191660011781556040516001600160a01b0383166024820152613cda90869060440160408051601f198184030181529190526020810180516001600160e01b0316631b2ce7f360e11b179052614821565b50805460ff19168155613ceb613bf2565b6001600160a01b0316826001600160a01b031614613d635760405162461bcd60e51b815260206004820152602f60248201527f45524331393637557067726164653a207570677261646520627265616b73206660448201526e75727468657220757067726164657360881b6064820152608401610aaf565b613d6c8561490c565b5050505050565b613d7b612c29565b33600081815261019160205260408082206001600160a01b03851683529082209092909190613da990613320565b9050613db53382613497565b6303b350f960e41b613dd2816001600160a01b03871660006132ed565b6001830154613e39906001600160f81b0316158015613dfd57506001840154600160f81b900460ff16155b8015613e0b57506004840154155b8015613e1957506002840154155b8015610f4257506003840154156001600160e01b0319831690600061360a565b600180850154855491850180546001600160f81b0319166001600160f81b039092169182179055600160801b8083046001600160801b0390811691820293169283178655610197546002870155609854600387015560048088018054939493613ea59289019190614fef565b506001870180546001600160f81b03191690556000808855613ecb9060048901906150de565b60018601548654604080516001600160f81b03878116825290931660208401526001600160801b038581168483015280831660608501528481166080850152600160801b90920490911660a0830152516001600160a01b038a169133917fb0ddca6540feadff203fffcc5cf7f1e9ba1bc3011800d146734f13e4ddfc7b6d9181900360c00190a35050505050505050565b806119c057604051630df66e3d60e41b81526001600160e01b031983166004820152336024820152604401610aaf565b60006302665fad60e41b6136cb816001600160401b03851115846144d2565b6001600160a01b03811660009081526101c260205260409020546334aae7c960e11b906119c090829060ff1615613f5c565b3360009081526101916020526040812090638081252560e01b90805b848110156142965761019554600090819081906001600160a01b0316632726b506338b8b8881811061403b57634e487b7160e01b600052603260045260246000fd5b6040516001600160e01b031960e087901b1681526001600160a01b039094166004850152602002919091013560248301525060440160a06040518083038186803b15801561408857600080fd5b505afa15801561409c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140c091906155bd565b94505093509350506140fb63623cc7e4836001600160401b031611158560036140e99190615883565b6001600160e01b03198916919061360a565b61412b6000836001600160401b0316118015614115575081155b614120866003615883565b6140e9906001615829565b33600090815261019260205260408120614190918b8b8881811061415f57634e487b7160e01b600052603260045260246000fd5b905060200201358152602001908152602001600020546000148560036141859190615883565b6140e9906002615829565b3360009081526101926020526040812084918b8b888181106141c257634e487b7160e01b600052603260045260246000fd5b9050602002013581526020019081526020016000208190555082856141e79190615829565b600188018054919650600160f81b90910460ff1690601f6142078361598b565b91906101000a81548160ff021916908360ff1602179055505088888581811061424057634e487b7160e01b600052603260045260246000fd5b9050602002013587600501600060018a600101601f9054906101000a900460ff1661426b9190615921565b60ff16815260200190815260200160002081905550505050808061428e90615970565b915050613ff9565b5060405181815233907fce2872a31020970c0a5aecb43e7199b699f079f2a54624c757b797e950621cb49060200160405180910390a25050505050565b33600090815261019160205260409020630cc1cb2160e31b6143096142f7876118e4565b6001600160e01b031983169015613f5c565b6040805160208082018990523360601b6bffffffffffffffffffffffff191682840152605482018890526074808301889052835180840390910181526094830180855281519183019190912060b4928c0280850184019095528b8252936143a793614394938e928e9283920190849080828437600092019190915250506101f454915085905061494c565b6001600160e01b031984169060006144d2565b60006143b38686614a09565b905060006143c4621e848083615883565b905060006143d5621e84808a615863565b90506143e96143e4838b615829565b6136d2565b6001870180546000906144069084906001600160f81b0316615807565b92506101000a8154816001600160f81b0302191690836001600160f81b0316021790555088826144369190615829565b61019860008282546144489190615829565b9091555061445890508184615829565b61019a600082825461446a9190615829565b909155505060088a901c60009081526101f5602052604090208054600160ff8d161b17905560405189815233907ff64e2f01a751ac603b52fb61346b9025275ca004ae639b4b29dc56d46c6f995f9060200160405180910390a2505050505050505050505050565b81611580576040516301cc826960e41b81526001600160e01b03198416600482015260ff82166024820152604401610aaf565b600054610100900460ff168061451e575060005460ff16155b61453a5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff1615801561455c576000805461ffff19166101011790555b630a895f0360e11b614579816001600160a01b03891660026132ed565b6145986001600160e01b031982166001600160a01b03881660036132ed565b6145b76001600160e01b031982166001600160401b03861660056132ed565b6145d76001600160e01b0319821663ffffffff808616906006906132ed16565b6145e085614bb0565b6145e8614c55565b6145f0614cc8565b5061019480546001600160a01b03199081166001600160a01b038b811691909117909255610193805482168a841617905561019680546101958054909316898516179092559188166001600160c01b031990911617600160a01b63ffffffff851602176001600160c01b0316600160c01b6001600160401b038616021790558015614681576000805461ff00191690555b5050505050505050565b600063809fdd3360e01b6136cb816001600160801b03851115846144d2565b60006146ff826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d2f9092919063ffffffff16565b805190915015611580578080602001905181019061471d919061550c565b6115805760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610aaf565b803b6147e05760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610aaf565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060823b6148805760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610aaf565b600080846001600160a01b03168460405161489b9190615684565b600060405180830381855af49150503d80600081146148d6576040519150601f19603f3d011682016040523d82523d6000602084013e6148db565b606091505b50915091506149038282604051806060016040528060278152602001615a2260279139614d3e565b95945050505050565b6149158161477c565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b600081815b85518110156149fe57600086828151811061497c57634e487b7160e01b600052603260045260246000fd5b602002602001015190508083116149be5760408051602081018590529081018290526060016040516020818303038152906040528051906020012092506149eb565b60408051602081018390529081018490526060016040516020818303038152906040528051906020012092505b50806149f681615970565b915050614951565b509092149392505050565b336000908152610191602052604081208215614a8a576065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca90614a53903390889088906004016156a0565b600060405180830381600087803b158015614a6d57600080fd5b505af1158015614a81573d6000803e3d6000fd5b50505050614b6d565b60006040518060800160405280614aa0876136ac565b6001600160781b03168152602001614ab742613f8c565b6001600160401b03168152602001614ad36301e1338042611749565b6001600160401b03908116825260016020928301819052600486018054918201815560009081528390208451910180549385015160408601516060909601511515600160f81b026001600160f81b03968516600160b81b02969096166001600160b81b0391909416600160781b026001600160b81b03199095166001600160781b0390931692909217939093171617919091179055508391505b60408051858152841515602082015233917f2f86bcc4da3458ee5f7dd62a4327b137bed761eee359b74cd58d64a09cb8589d910160405180910390a25092915050565b600054610100900460ff1680614bc9575060005460ff16155b614be55760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614c07576000805461ffff19166101011790555b63bb6c0dbf60e01b614c24816001600160a01b03851660006132ed565b50606580546001600160a01b0319166001600160a01b03841617905580156119c0576000805461ff00191690555050565b600054610100900460ff1680614c6e575060005460ff16155b614c8a5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614cac576000805461ffff19166101011790555b614cb4614d77565b8015610a59576000805461ff001916905550565b600054610100900460ff1680614ce1575060005460ff16155b614cfd5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614d1f576000805461ffff19166101011790555b614d27614de7565b614cb4614e51565b6060613bea8484600085614ec7565b60608315614d4d575081613b81565b825115614d5d5782518084602001fd5b8160405162461bcd60e51b8152600401610aaf91906156c3565b600054610100900460ff1680614d90575060005460ff16155b614dac5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614dce576000805461ffff19166101011790555b600160c9558015610a59576000805461ff001916905550565b600054610100900460ff1680614e00575060005460ff16155b614e1c5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614cb4576000805461ffff19166101011790558015610a59576000805461ff001916905550565b600054610100900460ff1680614e6a575060005460ff16155b614e865760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614ea8576000805461ffff19166101011790555b61012d805460ff191690558015610a59576000805461ff001916905550565b606082471015614f285760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610aaf565b843b614f765760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610aaf565b600080866001600160a01b03168587604051614f929190615684565b60006040518083038185875af1925050503d8060008114614fcf576040519150601f19603f3d011682016040523d82523d6000602084013e614fd4565b606091505b5091509150614fe4828286614d3e565b979650505050505050565b8280548282559060005260206000209081019282156150ce5760005260206000209182015b828111156150ce57825482546001600160781b039091166effffffffffffffffffffffffffffff1982168117845584546001600160b81b031990921617600160781b918290046001600160401b0390811690920217808455845467ffffffffffffffff60b81b198216600160b81b9182900490931602918217845584546001600160b81b039091166001600160f81b0390921691909117600160f81b9182900460ff16151590910217825560019283019290910190615014565b506150da9291506150f8565b5090565b5080546000825590600052602060002090810190610a5991905b5b808211156150da57600081556001016150f9565b60008083601f84011261511e578081fd5b5081356001600160401b03811115615134578182fd5b6020830191508360208260051b850101111561514f57600080fd5b9250929050565b600060208284031215615167578081fd5b8135613b81816159d7565b600060208284031215615183578081fd5b8151613b81816159d7565b600080600080600080600080610100898b0312156151aa578384fd5b88356151b5816159d7565b975060208901356151c5816159d7565b965060408901356151d5816159d7565b955060608901356151e5816159d7565b945060808901356151f581615a0c565b935060a0890135615205816159fa565b925060c0890135615215816159d7565b8092505060e089013590509295985092959890939650565b6000806040838503121561523f578182fd5b823561524a816159d7565b915060208301356001600160401b0380821115615265578283fd5b818501915085601f830112615278578283fd5b81358181111561528a5761528a6159c1565b604051601f8201601f19908116603f011681019083821181831017156152b2576152b26159c1565b816040528281528860208487010111156152ca578586fd5b82602086016020830137856020848301015280955050505050509250929050565b600080604083850312156152fd578182fd5b8235615308816159d7565b946020939093013593505050565b60008060208385031215615328578182fd5b82356001600160401b0381111561533d578283fd5b6153498582860161510d565b90969095509350505050565b6000806000806040858703121561536a578384fd5b84356001600160401b0380821115615380578586fd5b61538c8883890161510d565b909650945060208701359150808211156153a4578384fd5b506153b18782880161510d565b95989497509550505050565b60008060008060008060008060c0898b0312156153d8578182fd5b88356001600160401b03808211156153ee578384fd5b6153fa8c838d0161510d565b909a50985060208b0135975060408b013591506001600160f81b0382168214615421578384fd5b90955060608a0135945060808a01359061543a826159ec565b90935060a08a0135908082111561544f578384fd5b5061545c8b828c0161510d565b999c989b5096995094979396929594505050565b600080600060408486031215615484578081fd5b83356001600160401b038082111561549a578283fd5b818601915086601f8301126154ad578283fd5b8135818111156154bb578384fd5b8760208260061b85010111156154cf578384fd5b602092830195509350508401356154e5816159ec565b809150509250925092565b600060208284031215615501578081fd5b8135613b81816159ec565b60006020828403121561551d578081fd5b8151613b81816159ec565b600060208284031215615539578081fd5b5035919050565b600060208284031215615551578081fd5b81516001600160c01b0381168114613b81578182fd5b60008060408385031215615579578182fd5b50508035926020909101359150565b6000806000806080858703121561559d578182fd5b505082516020840151604085015160609095015191969095509092509050565b600080600080600060a086880312156155d4578283fd5b855194506020860151935060408601516155ed81615a0c565b60608701519093506155fe81615a0c565b608087015190925061560f816159ec565b809150509295509295909350565b6000806040838503121561562f578182fd5b82359150602083013561564181615a0c565b809150509250929050565b60006020828403121561565d578081fd5b8135613b81816159fa565b600060208284031215615679578081fd5b8151613b81816159fa565b60008251615696818460208701615944565b9190910192915050565b6001600160a01b0393909316835260208301919091521515604082015260600190565b60208152600082518060208401526156e2816040850160208701615944565b601f01601f19169190910160400192915050565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b19195b1959d85d1958d85b1b60a21b606082015260800190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b6163746976652070726f787960a01b606082015260800190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b60006001600160801b038083168185168083038211156157fe576157fe6159ab565b01949350505050565b60006001600160f81b038281168482168083038211156157fe576157fe6159ab565b6000821982111561583c5761583c6159ab565b500190565b60006001600160401b038083168185168083038211156157fe576157fe6159ab565b60008261587e57634e487b7160e01b81526012600452602481fd5b500490565b600081600019048311821515161561589d5761589d6159ab565b500290565b60006001600160781b03838116908316818110156158c2576158c26159ab565b039392505050565b60006001600160f81b03838116908316818110156158c2576158c26159ab565b6000828210156158fc576158fc6159ab565b500390565b60006001600160401b03838116908316818110156158c2576158c26159ab565b600060ff821660ff84168082101561593b5761593b6159ab565b90039392505050565b60005b8381101561595f578181015183820152602001615947565b83811115612b515750506000910152565b6000600019821415615984576159846159ab565b5060010190565b600060ff821660ff8114156159a2576159a26159ab565b60010192915050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610a5957600080fd5b8015158114610a5957600080fd5b63ffffffff81168114610a5957600080fd5b6001600160401b0381168114610a5957600080fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a164736f6c6343000804000a
Deployed Bytecode
0x6080604052600436106102ad5760003560e01c806370a0823111610175578063afe0b716116100dc578063dc60e2cb11610095578063ef0c79241161006f578063ef0c7924146109a2578063fa213bd6146109ea578063fe575a8714610a01578063fff6cae914610a3257600080fd5b8063dc60e2cb1461093b578063e5f83e6c14610974578063ed91b7de1461098b57600080fd5b8063afe0b7161461081f578063b1aca5041461083f578063bb3d676a1461085f578063cbdf382c1461087f578063ce111541146108b8578063cfd47663146108cf57600080fd5b80639e2c8a5b1161012e5780639e2c8a5b1461069e578063a1aab33f146106be578063a4292812146106f8578063a87430ba14610718578063ac89352a146107cc578063ae9f245a1461080857600080fd5b806370a08231146105c457806374bd128f146105e45780637cb64759146106045780638b323d1114610624578063909767d914610644578063952e68cf1461067e57600080fd5b80633659cfe611610219578063583c1aca116101d2578063583c1aca1461050b57806358c1900f1461052b5780635c7f74bb1461054b5780635c975abb1461056b5780636817031b146105845780636955cf92146105a457600080fd5b80633659cfe6146104585780633b350f90146104785780634087aeb71461049857806344cc892d146104b85780634568036e146104d85780634f1ef286146104f857600080fd5b80631da10d911161026b5780631da10d9114610372578063242693d31461039757806329eb5f2c146103ac5780632eb4a7ab146103ec5780633021a5601461040357806331d7a2621461042357600080fd5b806251ccba146102b257806302329a29146102d457806305472358146102f4578063114952be1461031d5780631965a5991461033d5780631bfe80b814610352575b600080fd5b3480156102be57600080fd5b506102d26102cd3660046154f0565b610a47565b005b3480156102e057600080fd5b506102d26102ef3660046154f0565b610a5c565b34801561030057600080fd5b5061030a60985481565b6040519081526020015b60405180910390f35b34801561032957600080fd5b506102d261033836600461518e565b610a7a565b34801561034957600080fd5b506102d2610b07565b34801561035e57600080fd5b506102d261036d366004615470565b610b1a565b34801561037e57600080fd5b50610387600081565b6040519015158152602001610314565b3480156103a357600080fd5b5061030a610ee8565b3480156103b857600080fd5b50610196546103d490600160c01b90046001600160401b031681565b6040516001600160401b039091168152602001610314565b3480156103f857600080fd5b5061030a6101f45481565b34801561040f57600080fd5b506102d261041e366004615528565b610f01565b34801561042f57600080fd5b5061044361043e366004615156565b610fd9565b60408051928352602083019190915201610314565b34801561046457600080fd5b506102d2610473366004615156565b61140b565b34801561048457600080fd5b506102d2610493366004615156565b6114d1565b3480156104a457600080fd5b506102d26104b336600461564c565b611585565b3480156104c457600080fd5b506102d26104d33660046152eb565b6115d4565b3480156104e457600080fd5b506103876104f3366004615528565b6118e4565b6102d261050636600461522d565b61190a565b34801561051757600080fd5b506102d2610526366004615316565b6119c4565b34801561053757600080fd5b506102d26105463660046153bd565b611d0c565b34801561055757600080fd5b506102d2610566366004615355565b611d64565b34801561057757600080fd5b5061012d5460ff16610387565b34801561059057600080fd5b506102d261059f366004615156565b611fec565b3480156105b057600080fd5b506102d26105bf366004615316565b61211a565b3480156105d057600080fd5b5061030a6105df366004615156565b61214c565b3480156105f057600080fd5b506102d26105ff366004615528565b6121d0565b34801561061057600080fd5b506102d261061f366004615528565b6121de565b34801561063057600080fd5b506102d261063f366004615316565b6121ec565b34801561065057600080fd5b5061030a61065f366004615156565b6001600160a01b03166000908152610191602052604090206004015490565b34801561068a57600080fd5b506102d261069936600461561d565b6123d2565b3480156106aa57600080fd5b506102d26106b9366004615567565b612701565b3480156106ca57600080fd5b50610196546106e390600160a01b900463ffffffff1681565b60405163ffffffff9091168152602001610314565b34801561070457600080fd5b506102d26107133660046154f0565b6129dc565b34801561072457600080fd5b50610783610733366004615156565b6101916020526000908152604090208054600182015460028301546003909301546001600160801b0380841694600160801b90940416926001600160f81b03831692600160f81b900460ff169186565b604080516001600160801b0397881681529690951660208701526001600160f81b039093169385019390935260ff166060840152608083019190915260a082015260c001610314565b3480156107d857600080fd5b506103876107e73660046152eb565b6101f660209081526000928352604080842090915290825290205460ff1681565b34801561081457600080fd5b5061030a6101985481565b34801561082b57600080fd5b506102d261083a366004615528565b6129ed565b34801561084b57600080fd5b5061030a61085a3660046152eb565b6129fb565b34801561086b57600080fd5b506102d261087a366004615316565b612a65565b34801561088b57600080fd5b50610196546108a0906001600160a01b031681565b6040516001600160a01b039091168152602001610314565b3480156108c457600080fd5b5061030a61019a5481565b3480156108db57600080fd5b506108ef6108ea3660046152eb565b612b57565b6040805182516001600160781b031681526020808401516001600160401b0390811691830191909152838301511691810191909152606091820151151591810191909152608001610314565b34801561094757600080fd5b5061030a6109563660046152eb565b61019260209081526000928352604080842090915290825290205481565b34801561098057600080fd5b5061030a6101f75481565b34801561099757600080fd5b5061030a6101995481565b3480156109ae57600080fd5b5061030a6109bd3660046152eb565b6001600160a01b039190911660009081526101916020908152604080832093835260059093019052205490565b3480156109f657600080fd5b5061030a6101975481565b348015610a0d57600080fd5b50610387610a1c366004615156565b6101c26020526000908152604090205460ff1681565b348015610a3e57600080fd5b506102d2612c19565b610a4f612c29565b610a593382612c55565b50565b610a64612ff2565b8015610a7257610a59613098565b610a59613132565b600054610100900460ff1680610a93575060005460ff16155b610ab85760405162461bcd60e51b8152600401610aaf9061578e565b60405180910390fd5b600054610100900460ff16158015610ada576000805461ffff19166101011790555b610aea898989868a8a8a896131ae565b8015610afc576000805461ff00191690555b505050505050505050565b610b0f612c29565b610b1833613236565b565b610b22612c29565b63037fd01760e31b610b36818460006132ed565b3360008181526101916020526040812091610b5090613320565b9050610b5c3382613497565b60008060005b87811015610d9b576000808a8a84818110610b8d57634e487b7160e01b600052603260045260246000fd5b905060400201600001358b8b85818110610bb757634e487b7160e01b600052603260045260246000fd5b90506040020160200135915091506000876004018381548110610bea57634e487b7160e01b600052603260045260246000fd5b60009182526020909120018054909150610c2d90600160b81b90046001600160401b03164211610c1b866003615883565b6001600160e01b03198c16919061360a565b610c436001600160e01b03198a168360016132ed565b80546001600160781b03811690600160f81b900460ff16610c8c8c151582151514610c6f886003615883565b610c7a906001615829565b6001600160e01b03198e16919061360a565b610cb26001600160781b038316851115610ca7886003615883565b610c7a906002615829565b6000610cbd8461363b565b90506000610cd4866001600160781b0386166158ea565b610d0b578b6004018781548110610cfb57634e487b7160e01b600052603260045260246000fd5b6000918252602082200155610d5f565b610d14866136ac565b85548690600090610d2f9084906001600160781b03166158a2565b92506101000a8154816001600160781b0302191690836001600160781b03160217905550610d5c8561363b565b90505b610d6981836158ea565b610d73908b615829565b9950610d7f868a615829565b9850505050505050508080610d9390615970565b915050610b62565b50610da5826136d2565b600185018054600090610dc29084906001600160f81b03166158ca565b92506101000a8154816001600160f81b0302191690836001600160f81b03160217905550816101986000828254610df991906158ea565b925050819055508061019a6000828254610e1391906158ea565b90915550508515610e8a576065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca90610e5390339085906000906004016156a0565b600060405180830381600087803b158015610e6d57600080fd5b505af1158015610e81573d6000803e3d6000fd5b50505050610ea2565b61019654610ea2906001600160a01b031633836136f1565b60408051828152871515602082015233917f30d438cf38db3f29630029343ab01e801276913697f489030c0613ddc4aadaaf910160405180910390a25050505050505050565b60006101f75461019a54610efc9190615829565b905090565b610f09613754565b610f11612c29565b610f19613b3f565b6301810d2b60e51b81610f2a575050565b610f556000610198541180610f425750600061019954115b6001600160e01b0319831690600061360a565b610f716101995461019854610f6a9190615829565b8390613b61565b60986000828254610f829190615829565b909155505061019454610fa0906001600160a01b0316333085613b88565b60405182815233907f55fd0bec59e0b2fdf9406c9890f568c6ec6f92c752012ef7e3ebaee44d4d6cad9060200160405180910390a25050565b600080610ff76318ebd13160e11b6001600160a01b038516836132ed565b610196546001600160a01b03841660009081526101916020526040812060018101549192600160c01b90046001600160401b031691600160f81b810460ff16906001600160f81b031684821561114d5760005b8381101561114b576000818152600586016020526040808220546101955491516313935a8360e11b81526001600160a01b038e81166004830152602482018390529193929190911690632726b5069060440160a06040518083038186803b1580156110b457600080fd5b505afa1580156110c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ec91906155bd565b5050506001600160a01b038e166000908152610192602090815260408083208784529091529020549092509050808211156111275780611129565b815b6111339086615829565b9450505050808061114390615970565b91505061104a565b505b844211801561115e57506101985415155b1561138b57606554604080516318cbe5db60e11b815290516000926001600160a01b031691633197cbb6916004808301926020929190829003018186803b1580156111a857600080fd5b505afa1580156111bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e09190615668565b63ffffffff1690506000814211611200576111fb87426158ea565b61120a565b61120a87836158ea565b90506000606560009054906101000a90046001600160a01b03166001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b15801561125c57600080fd5b505afa158015611270573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112949190615668565b63ffffffff16606560009054906101000a90046001600160a01b03166001600160a01b0316634251342f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156112e857600080fd5b505afa1580156112fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113209190615540565b610196546001600160c01b03919091169061134890600160a01b900463ffffffff1685615883565b6113529190615883565b61135c9190615863565b9050610197546113776101995461019854610f6a9190615829565b6113819190615829565b9850505050611392565b6101975495505b835460028501546001600160801b03909116906113bc9088906113b58587615829565b9190613bc0565b6113c69190615829565b84546098546003870154929a50600160801b9091046001600160801b0316916113f491906113b58587615829565b6113fe9190615829565b9650505050505050915091565b306001600160a01b037f000000000000000000000000db0a2a38f30a3f43fbfae98c85b132702df20cb31614156114545760405162461bcd60e51b8152600401610aaf906156f6565b7f000000000000000000000000db0a2a38f30a3f43fbfae98c85b132702df20cb36001600160a01b0316611486613bf2565b6001600160a01b0316146114ac5760405162461bcd60e51b8152600401610aaf90615742565b6114b581613c20565b60408051600080825260208201909252610a5991839190613c28565b6101955460405163543a185d60e11b81523360048201526303b350f960e41b916000916001600160a01b039091169063a87430ba9060240160806040518083038186803b15801561152157600080fd5b505afa158015611535573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115599190615588565b509092506115779150506001600160e01b031983168215600061360a565b61158083613d73565b505050565b61158d613754565b6065546115ad90634087aeb760e01b906001600160a01b03163314613f5c565b610196805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b600260c95414156116275760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610aaf565b600260c955611634612c29565b606554604051631e1c6a0760e01b81523360048201526116bf916001600160a01b031690631e1c6a079060240160206040518083038186803b15801561167957600080fd5b505afa15801561168d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b1919061550c565b6344cc892d60e01b90613f5c565b6001600160a01b038216600090815261019160205260408120906116e284613320565b90506116ee8482613497565b60006116fd621e848085615883565b905060006040518060800160405280611715876136ac565b6001600160781b0316815260200161173142613f8c565b613f8c565b6001600160401b031681526020016117536301e13380425b61172c9190615829565b6001600160401b0316815260016020909101529050611771826136d2565b60018501805460009061178e9084906001600160f81b0316615807565b82546101009290920a6001600160f81b038181021990931691831602179091556004860180546001810182556000918252602080832086519201805491870151604088015160608901516001600160781b039095166001600160b81b031990941693909317600160781b6001600160401b0392831602176001600160b81b0316600160b81b919093160290941617600160f81b91151591909102179091556101988054859350909190611842908490615829565b925050819055508461019a600082825461185c9190615829565b909155505060048401546001600160a01b0387169033907f47ee2438657f63a52980ec1ed6e4424262f81f621308d91694c410db5bfa401b906118a1906001906158ea565b886118b06301e1338042611749565b6040805193845260208401929092526001600160401b03169082015260600160405180910390a35050600160c95550505050565b600881901c60009081526101f56020526040812054600160ff84161b1615155b92915050565b306001600160a01b037f000000000000000000000000db0a2a38f30a3f43fbfae98c85b132702df20cb31614156119535760405162461bcd60e51b8152600401610aaf906156f6565b7f000000000000000000000000db0a2a38f30a3f43fbfae98c85b132702df20cb36001600160a01b0316611985613bf2565b6001600160a01b0316146119ab5760405162461bcd60e51b8152600401610aaf90615742565b6119b482613c20565b6119c082826001613c28565b5050565b632c1e0d6560e11b6119d533613fab565b6119dd612c29565b33600081815261019160205260408120918190819081906119fd90613320565b9050611a093382613497565b905060005b86811015611bdf576000888883818110611a3857634e487b7160e01b600052603260045260246000fd5b610195546040516313935a8360e11b8152336004820152602090920293909301356024820181905293506000928392508291829182916001600160a01b0390911690632726b5069060440160a06040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ad391906155bd565b94509450945094509450611b0081886003611aee9190615883565b6001600160e01b03198f16919061360a565b611b256001600160401b0383164211611b1a896004615883565b611aee906001615829565b3360009081526101f660209081526040808320898452909152902054611b5f9060ff1615611b54896005615883565b611aee906002615829565b6101c354611b88906001600160401b03851610611b7d896006615883565b611aee906003615829565b3360009081526101f6602090815260408083208984529091529020805460ff19166001179055611bb8858b615829565b9950611bc4848a615829565b98505050505050508080611bd790615970565b915050611a0e565b50611be9826136d2565b600185018054600090611c069084906001600160f81b03166158ca565b92506101000a8154816001600160f81b0302191690836001600160f81b03160217905550816101986000828254611c3d91906158ea565b90915550611c509050621e848083615863565b61019a6000828254611c6291906158ea565b90915550506065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca90611c9c90339087906000906004016156a0565b600060405180830381600087803b158015611cb657600080fd5b505af1158015611cca573d6000803e3d6000fd5b50506040518581523392507f28700c38b1cd61046b9139c648b8f2c6959575cce07dcc3556ddd4906e0a6dac915060200160405180910390a250505050505050565b611d1533613fab565b611d1d612c29565b6000611d2833613320565b9050611d343382613497565b611d3e8383613fdd565b6001600160f81b03861615610afc57610afc898989896001600160f81b031689896142d3565b611d6c612c29565b635c7f74bb60e01b611d828185841460006144d2565b60005b84811015611fe4576000868683818110611daf57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611dc49190615156565b606554604051631e1c6a0760e01b81526001600160a01b038084166004830152929350611e599290911690631e1c6a079060240160206040518083038186803b158015611e1057600080fd5b505afa158015611e24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e48919061550c565b6001600160e01b0319851690613f5c565b61019454604080516332f7ce0b60e21b815290516001600160a01b039283169284169163cbdf382c916004808301926020929190829003018186803b158015611ea157600080fd5b505afa158015611eb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed99190615172565b6001600160a01b03161415611f2b57611f2633868685818110611f0c57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611f2191906154f0565b612c55565b611fd1565b806001600160a01b03166353961e0633878786818110611f5b57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611f7091906154f0565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015215156024820152604401600060405180830381600087803b158015611fb857600080fd5b505af1158015611fcc573d6000803e3d6000fd5b505050505b5080611fdc81615970565b915050611d85565b505050505050565b60655460408051638da5cb5b60e01b81529051636817031b60e01b926120989233926001600160a01b0390921691638da5cb5b91600480820192602092909190829003018186803b15801561204057600080fd5b505afa158015612054573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120789190615172565b6001600160e01b03198416916001600160a01b039190911614600061360a565b6120b96001600160e01b031982166001600160a01b038416151560006144d2565b609780546001600160a01b0319166001600160a01b038416908117909155604080518281526020810192909252839133917faa55cb88c5664f8f0bbaf57c2fb02459d3a8dd15bfc1e4490335358804da3234910160405180910390a2505050565b61212333613fab565b61212b612c29565b600061213633613320565b90506121423382613497565b6115808383613fdd565b6001600160a01b038116600090815261019160205260408120815b60048201548110156121c95781600401818154811061219657634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546121b5906001600160781b031684615829565b9250806121c181615970565b915050612167565b5050919050565b6121d8612ff2565b61019955565b6121e6612ff2565b6101f455565b6121f4612c29565b60005b8181101561158057600083838381811061222157634e487b7160e01b600052603260045260246000fd5b90506020020160208101906122369190615156565b606554604051631e1c6a0760e01b81526001600160a01b0380841660048301529293506122c89290911690631e1c6a079060240160206040518083038186803b15801561228257600080fd5b505afa158015612296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ba919061550c565b638b323d1160e01b90613f5c565b61019454604080516332f7ce0b60e21b815290516001600160a01b039283169284169163cbdf382c916004808301926020929190829003018186803b15801561231057600080fd5b505afa158015612324573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123489190615172565b6001600160a01b031614156123655761236033613236565b6123bf565b604051630834ef4560e01b81523360048201526001600160a01b03821690630834ef4590602401600060405180830381600087803b1580156123a657600080fd5b505af11580156123ba573d6000803e3d6000fd5b505050505b50806123ca81615970565b9150506121f7565b600260c95414156124255760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610aaf565b600260c955612432612c29565b63952e68cf60e01b612446818460016132ed565b61248562278d00836001600160401b03161015801561247257506301e13380836001600160401b031611155b6001600160e01b031983169060026144d2565b336000818152610191602052604081209161249f90613320565b90506124ab3382613497565b6000846124b742613f8c565b6124c19190615841565b9050600086620f42406301e13380816124e3426001600160401b0388166158ea565b6124ed9190615883565b6124f79190615863565b6125019190615829565b61250b9190615883565b90506000811161252b57634e487b7160e01b600052600160045260246000fd5b600060405180608001604052806125418a6136ac565b6001600160781b0316815260200161255842613f8c565b6001600160401b0390811682528581166020808401919091526000604093840181905260048a01805460018101825590825290829020855191018054928601519486015160608701511515600160f81b026001600160f81b03918616600160b81b02919091166001600160b81b0396909516600160781b026001600160b81b03199094166001600160781b03909316929092179290921793909316919091179190911790559050612608826136d2565b6001860180546000906126259084906001600160f81b0316615807565b92506101000a8154816001600160f81b0302191690836001600160f81b0316021790555081610198600082825461265c9190615829565b925050819055508761019a60008282546126769190615829565b909155505061019654612694906001600160a01b031633308b613b88565b6004850154339081907f47ee2438657f63a52980ec1ed6e4424262f81f621308d91694c410db5bfa401b906126cb906001906158ea565b60408051918252602082018d90526001600160401b0388169082015260600160405180910390a35050600160c955505050505050565b612709612c29565b639e2c8a5b60e01b61271d818360006132ed565b336000908152610191602052604081206004810180549192918690811061275457634e487b7160e01b600052603260045260246000fd5b6000918252602090912001805490915061278d90600160b81b90046001600160401b0316426001600160e01b031986169110600061360a565b80546001600160781b03811690600160f81b900460ff166127bd6001600160e01b031986168784101560016144d2565b60006127c833613320565b90506127d43382613497565b60006127df8561363b565b905060006127f6896001600160781b0387166158ea565b61282d57866004018a8154811061281d57634e487b7160e01b600052603260045260246000fd5b6000918252602082200155612881565b612836896136ac565b865487906000906128519084906001600160781b03166158a2565b92506101000a8154816001600160781b0302191690836001600160781b0316021790555061287e8661363b565b90505b6001870154819061289c9084906001600160f81b03166158ea565b6128a69190615829565b6001880180546001600160f81b0319166001600160f81b03929092169190911790556101985481906128d99084906158ea565b6128e39190615829565b610198819055508861019a60008282546128fd91906158ea565b90915550508315612974576065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca9061293d9033908d906000906004016156a0565b600060405180830381600087803b15801561295757600080fd5b505af115801561296b573d6000803e3d6000fd5b5050505061298c565b6101965461298c906001600160a01b0316338b6136f1565b604080518b8152602081018b905285151581830152905133917f729ab3b283445a8f221b97237248ecc12a89cafc93156a0c77e1174a01ef1c16919081900360600190a250505050505050505050565b6129e4612c29565b610a4f33613236565b6129f5612ff2565b6101f755565b6001600160a01b038216600090815261019160205260408120815b6001820154600160f81b900460ff16811015612a5f576000818152600583016020526040902054841415612a4d5791506119049050565b80612a5781615970565b915050612a16565b50600080fd5b612a6d612ff2565b635d9eb3b560e11b60005b82811015612b5157612adb6000858584818110612aa557634e487b7160e01b600052603260045260246000fd5b9050602002016020810190612aba9190615156565b6001600160e01b03198516916001600160a01b0391909116141560006144d2565b60016101c26000868685818110612b0257634e487b7160e01b600052603260045260246000fd5b9050602002016020810190612b179190615156565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905580612b4981615970565b915050612a78565b50505050565b6040805160808101825260008082526020808301829052828401829052606083018290526001600160a01b0386168252610191905291909120600401805483908110612bb357634e487b7160e01b600052603260045260246000fd5b60009182526020918290206040805160808101825292909101546001600160781b0381168352600160781b81046001600160401b0390811694840194909452600160b81b810490931690820152600160f81b90910460ff16151560608201529392505050565b612c21612c29565b610b18613754565b63abb87a6f60e01b610a59612c4161012d5460ff1690565b6001600160e01b031983169015600061360a565b6001600160a01b03821660009081526101916020526040812090612c7884613320565b9050612c848482613497565b81546001600160801b031680612c9b575050505050565b82546fffffffffffffffffffffffffffffffff191683558315612d24576065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca90612ced90889085906001906004016156a0565b600060405180830381600087803b158015612d0757600080fd5b505af1158015612d1b573d6000803e3d6000fd5b50505050612fa3565b61019454610196546001600160a01b0390811691161415612eb6576000612d4e621e848083615883565b905060006040518060800160405280612d66856136ac565b6001600160781b03168152602001612d7d42613f8c565b6001600160401b03168152602001612d996301e1338042611749565b6001600160401b039081168252600160209283018190526004890180549182018155600090815283902084519101805493850151604086015160608701511515600160f81b026001600160f81b03918616600160b81b02919091166001600160b81b0392909516600160781b026001600160b81b03199096166001600160781b03909416939093179490941793909316919091171790559050612e3b826136d2565b600186018054600090612e589084906001600160f81b0316615807565b92506101000a8154816001600160f81b0302191690836001600160f81b03160217905550816101986000828254612e8f9190615829565b925050819055508261019a6000828254612ea99190615829565b90915550612fa392505050565b6065546101945460405163091465f760e11b81526001600160a01b0391821660048201526000929190911690631228cbee9060240160206040518083038186803b158015612f0357600080fd5b505afa158015612f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f3b9190615172565b6040516344cc892d60e01b81526001600160a01b03888116600483015260248201859052919250908216906344cc892d90604401600060405180830381600087803b158015612f8957600080fd5b505af1158015612f9d573d6000803e3d6000fd5b50505050505b604080518515158152602081018390526001600160a01b0387169133917f7aa2446843f85ab4372b9a9eddbe072a35cd062fb199eaddea2ad3b8d0396fa2910160405180910390a35050505050565b60655460408051638da5cb5b60e01b815290516339e71deb60e01b92610a59926001600160a01b0390911691638da5cb5b91600480820192602092909190829003018186803b15801561304457600080fd5b505afa158015613058573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307c9190615172565b6001600160e01b03198316906001600160a01b03163314613f5c565b61012d5460ff16156130df5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610aaf565b61012d805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586131153390565b6040516001600160a01b03909116815260200160405180910390a1565b61012d5460ff1661317c5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610aaf565b61012d805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613115565b600054610100900460ff16806131c7575060005460ff16155b6131e35760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015613205576000805461ffff19166101011790555b61321489898989898989614505565b6101c38290558015610afc576000805461ff0019169055505050505050505050565b6001600160a01b0381166000908152610191602052604081209061325983613320565b90506132658382613497565b8154600160801b90046001600160801b0316806132825750505050565b82546001600160801b03168355610194546132a7906001600160a01b031685836136f1565b6040518181526001600160a01b0385169033907fa69db2085ef89718710bae5b78f2032cf155ff99feca17d718e69f8ba375c11e9060200160405180910390a350505050565b8161158057604051633bd8dd9360e21b81526001600160e01b03198416600482015260ff82166024820152604401610aaf565b6001600160a01b0381166000908152610191602052604081206001810154600160f81b900460ff1680156121c95760005b8181101561348f576000818152600584016020526040808220546101955491516313935a8360e11b81526001600160a01b038981166004830152602482018390529193929190911690632726b5069060440160a06040518083038186803b1580156133bb57600080fd5b505afa1580156133cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133f391906155bd565b5050506001600160a01b03891660009081526101926020908152604080832087845290915290205490925090508082111561342e5780613430565b815b61343a9088615829565b9650818111156134795781156134505781613453565b60015b6001600160a01b0389166000908152610192602090815260408083208784529091529020555b505050808061348790615970565b915050613351565b505050919050565b61349f613754565b6001600160a01b03821660009081526101916020526040812060018101549091906134d49084906001600160f81b0316615829565b905060006134f461019754846002015484613bc09092919063ffffffff16565b90506000613513609854856003015485613bc09092919063ffffffff16565b905061351e8261468b565b845485906000906135399084906001600160801b03166157dc565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506135668161468b565b84548590601090613588908490600160801b90046001600160801b03166157dc565b82546001600160801b039182166101009390930a928302919092021990911617905550610197546002850155609854600385015560408051838152602081018390526001600160a01b0388169133917f31eeb6a0d26c29b4c243d704ff4ae6feebcc2e8b123df7ea0bd12083c3083cb8910160405180910390a3505050505050565b8161158057604051631db762fb60e31b81526001600160e01b03198416600482015260248101829052604401610aaf565b80546000906001600160781b03811690620f4240906301e1338090829061367b906001600160401b03600160781b8204811691600160b81b900416615901565b6001600160401b031661368e9190615883565b6136989190615863565b6136a29190615829565b6119049190615883565b6000631e4e4bad60e01b6136cb816001600160781b03851115846144d2565b5090919050565b6000631feb933960e11b6136cb816001600160f81b03851115846144d2565b6040516001600160a01b03831660248201526044810182905261158090849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526146aa565b60655460408051639f1dc9bd60e01b815290516001600160a01b03909216918291639f1dc9bd916004808301926020929190829003018186803b15801561379a57600080fd5b505afa1580156137ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137d2919061550c565b1561382b57806001600160a01b031663a83c76646040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561381257600080fd5b505af1158015613826573d6000803e3d6000fd5b505050505b6000816001600160a01b0316633197cbb66040518163ffffffff1660e01b815260040160206040518083038186803b15801561386657600080fd5b505afa15801561387a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061389e9190615668565b6101965463ffffffff919091169150600160c01b90046001600160401b031681116138c7575050565b61019654600160c01b90046001600160401b031642116138e5575050565b610198541580156138f7575061019954155b156139305761390542613f8c565b61019660186101000a8154816001600160401b0302191690836001600160401b031602179055505050565b600081421161393f5742613941565b815b6101965490915060009061396590600160c01b90046001600160401b0316836158ea565b90506000846001600160a01b0316634251342f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156139a257600080fd5b505afa1580156139b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139da9190615540565b6001600160c01b031690506000856001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b158015613a2057600080fd5b505afa158015613a34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a589190615668565b6101965463ffffffff91821691600160a01b90910416613a788486615883565b613a829190615883565b613a8c9190615863565b9050613aaa6101995461019854613aa39190615829565b8290613b61565b6101976000828254613abc9190615829565b90915550613acb905084613f8c565b61019680546001600160c01b0316600160c01b6001600160401b0393841681029190911791829055610197546040805191825291909204909216602082015233917f33e6f269701b611439c5bd9eae485d1b2f10d29b632a6f0d5688c93c2d77af1f910160405180910390a2505050505050565b60975463eeea774b60e01b90610a599082906001600160a01b03163314613f5c565b600081613b7768056bc75e2d6310000085615883565b613b819190615863565b9392505050565b6040516001600160a01b0380851660248301528316604482015260648101829052612b519085906323b872dd60e01b9060840161371d565b600068056bc75e2d63100000613bd683856158ea565b613be09086615883565b613bea9190615863565b949350505050565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b610a59612ff2565b6000613c32613bf2565b9050613c3d8461477c565b600083511180613c4a5750815b15613c5b57613c598484614821565b505b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143805460ff16613d6c57805460ff191660011781556040516001600160a01b0383166024820152613cda90869060440160408051601f198184030181529190526020810180516001600160e01b0316631b2ce7f360e11b179052614821565b50805460ff19168155613ceb613bf2565b6001600160a01b0316826001600160a01b031614613d635760405162461bcd60e51b815260206004820152602f60248201527f45524331393637557067726164653a207570677261646520627265616b73206660448201526e75727468657220757067726164657360881b6064820152608401610aaf565b613d6c8561490c565b5050505050565b613d7b612c29565b33600081815261019160205260408082206001600160a01b03851683529082209092909190613da990613320565b9050613db53382613497565b6303b350f960e41b613dd2816001600160a01b03871660006132ed565b6001830154613e39906001600160f81b0316158015613dfd57506001840154600160f81b900460ff16155b8015613e0b57506004840154155b8015613e1957506002840154155b8015610f4257506003840154156001600160e01b0319831690600061360a565b600180850154855491850180546001600160f81b0319166001600160f81b039092169182179055600160801b8083046001600160801b0390811691820293169283178655610197546002870155609854600387015560048088018054939493613ea59289019190614fef565b506001870180546001600160f81b03191690556000808855613ecb9060048901906150de565b60018601548654604080516001600160f81b03878116825290931660208401526001600160801b038581168483015280831660608501528481166080850152600160801b90920490911660a0830152516001600160a01b038a169133917fb0ddca6540feadff203fffcc5cf7f1e9ba1bc3011800d146734f13e4ddfc7b6d9181900360c00190a35050505050505050565b806119c057604051630df66e3d60e41b81526001600160e01b031983166004820152336024820152604401610aaf565b60006302665fad60e41b6136cb816001600160401b03851115846144d2565b6001600160a01b03811660009081526101c260205260409020546334aae7c960e11b906119c090829060ff1615613f5c565b3360009081526101916020526040812090638081252560e01b90805b848110156142965761019554600090819081906001600160a01b0316632726b506338b8b8881811061403b57634e487b7160e01b600052603260045260246000fd5b6040516001600160e01b031960e087901b1681526001600160a01b039094166004850152602002919091013560248301525060440160a06040518083038186803b15801561408857600080fd5b505afa15801561409c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140c091906155bd565b94505093509350506140fb63623cc7e4836001600160401b031611158560036140e99190615883565b6001600160e01b03198916919061360a565b61412b6000836001600160401b0316118015614115575081155b614120866003615883565b6140e9906001615829565b33600090815261019260205260408120614190918b8b8881811061415f57634e487b7160e01b600052603260045260246000fd5b905060200201358152602001908152602001600020546000148560036141859190615883565b6140e9906002615829565b3360009081526101926020526040812084918b8b888181106141c257634e487b7160e01b600052603260045260246000fd5b9050602002013581526020019081526020016000208190555082856141e79190615829565b600188018054919650600160f81b90910460ff1690601f6142078361598b565b91906101000a81548160ff021916908360ff1602179055505088888581811061424057634e487b7160e01b600052603260045260246000fd5b9050602002013587600501600060018a600101601f9054906101000a900460ff1661426b9190615921565b60ff16815260200190815260200160002081905550505050808061428e90615970565b915050613ff9565b5060405181815233907fce2872a31020970c0a5aecb43e7199b699f079f2a54624c757b797e950621cb49060200160405180910390a25050505050565b33600090815261019160205260409020630cc1cb2160e31b6143096142f7876118e4565b6001600160e01b031983169015613f5c565b6040805160208082018990523360601b6bffffffffffffffffffffffff191682840152605482018890526074808301889052835180840390910181526094830180855281519183019190912060b4928c0280850184019095528b8252936143a793614394938e928e9283920190849080828437600092019190915250506101f454915085905061494c565b6001600160e01b031984169060006144d2565b60006143b38686614a09565b905060006143c4621e848083615883565b905060006143d5621e84808a615863565b90506143e96143e4838b615829565b6136d2565b6001870180546000906144069084906001600160f81b0316615807565b92506101000a8154816001600160f81b0302191690836001600160f81b0316021790555088826144369190615829565b61019860008282546144489190615829565b9091555061445890508184615829565b61019a600082825461446a9190615829565b909155505060088a901c60009081526101f5602052604090208054600160ff8d161b17905560405189815233907ff64e2f01a751ac603b52fb61346b9025275ca004ae639b4b29dc56d46c6f995f9060200160405180910390a2505050505050505050505050565b81611580576040516301cc826960e41b81526001600160e01b03198416600482015260ff82166024820152604401610aaf565b600054610100900460ff168061451e575060005460ff16155b61453a5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff1615801561455c576000805461ffff19166101011790555b630a895f0360e11b614579816001600160a01b03891660026132ed565b6145986001600160e01b031982166001600160a01b03881660036132ed565b6145b76001600160e01b031982166001600160401b03861660056132ed565b6145d76001600160e01b0319821663ffffffff808616906006906132ed16565b6145e085614bb0565b6145e8614c55565b6145f0614cc8565b5061019480546001600160a01b03199081166001600160a01b038b811691909117909255610193805482168a841617905561019680546101958054909316898516179092559188166001600160c01b031990911617600160a01b63ffffffff851602176001600160c01b0316600160c01b6001600160401b038616021790558015614681576000805461ff00191690555b5050505050505050565b600063809fdd3360e01b6136cb816001600160801b03851115846144d2565b60006146ff826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d2f9092919063ffffffff16565b805190915015611580578080602001905181019061471d919061550c565b6115805760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610aaf565b803b6147e05760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610aaf565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060823b6148805760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610aaf565b600080846001600160a01b03168460405161489b9190615684565b600060405180830381855af49150503d80600081146148d6576040519150601f19603f3d011682016040523d82523d6000602084013e6148db565b606091505b50915091506149038282604051806060016040528060278152602001615a2260279139614d3e565b95945050505050565b6149158161477c565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b600081815b85518110156149fe57600086828151811061497c57634e487b7160e01b600052603260045260246000fd5b602002602001015190508083116149be5760408051602081018590529081018290526060016040516020818303038152906040528051906020012092506149eb565b60408051602081018390529081018490526060016040516020818303038152906040528051906020012092505b50806149f681615970565b915050614951565b509092149392505050565b336000908152610191602052604081208215614a8a576065546040516362cf156560e11b81526001600160a01b039091169063c59e2aca90614a53903390889088906004016156a0565b600060405180830381600087803b158015614a6d57600080fd5b505af1158015614a81573d6000803e3d6000fd5b50505050614b6d565b60006040518060800160405280614aa0876136ac565b6001600160781b03168152602001614ab742613f8c565b6001600160401b03168152602001614ad36301e1338042611749565b6001600160401b03908116825260016020928301819052600486018054918201815560009081528390208451910180549385015160408601516060909601511515600160f81b026001600160f81b03968516600160b81b02969096166001600160b81b0391909416600160781b026001600160b81b03199095166001600160781b0390931692909217939093171617919091179055508391505b60408051858152841515602082015233917f2f86bcc4da3458ee5f7dd62a4327b137bed761eee359b74cd58d64a09cb8589d910160405180910390a25092915050565b600054610100900460ff1680614bc9575060005460ff16155b614be55760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614c07576000805461ffff19166101011790555b63bb6c0dbf60e01b614c24816001600160a01b03851660006132ed565b50606580546001600160a01b0319166001600160a01b03841617905580156119c0576000805461ff00191690555050565b600054610100900460ff1680614c6e575060005460ff16155b614c8a5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614cac576000805461ffff19166101011790555b614cb4614d77565b8015610a59576000805461ff001916905550565b600054610100900460ff1680614ce1575060005460ff16155b614cfd5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614d1f576000805461ffff19166101011790555b614d27614de7565b614cb4614e51565b6060613bea8484600085614ec7565b60608315614d4d575081613b81565b825115614d5d5782518084602001fd5b8160405162461bcd60e51b8152600401610aaf91906156c3565b600054610100900460ff1680614d90575060005460ff16155b614dac5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614dce576000805461ffff19166101011790555b600160c9558015610a59576000805461ff001916905550565b600054610100900460ff1680614e00575060005460ff16155b614e1c5760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614cb4576000805461ffff19166101011790558015610a59576000805461ff001916905550565b600054610100900460ff1680614e6a575060005460ff16155b614e865760405162461bcd60e51b8152600401610aaf9061578e565b600054610100900460ff16158015614ea8576000805461ffff19166101011790555b61012d805460ff191690558015610a59576000805461ff001916905550565b606082471015614f285760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610aaf565b843b614f765760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610aaf565b600080866001600160a01b03168587604051614f929190615684565b60006040518083038185875af1925050503d8060008114614fcf576040519150601f19603f3d011682016040523d82523d6000602084013e614fd4565b606091505b5091509150614fe4828286614d3e565b979650505050505050565b8280548282559060005260206000209081019282156150ce5760005260206000209182015b828111156150ce57825482546001600160781b039091166effffffffffffffffffffffffffffff1982168117845584546001600160b81b031990921617600160781b918290046001600160401b0390811690920217808455845467ffffffffffffffff60b81b198216600160b81b9182900490931602918217845584546001600160b81b039091166001600160f81b0390921691909117600160f81b9182900460ff16151590910217825560019283019290910190615014565b506150da9291506150f8565b5090565b5080546000825590600052602060002090810190610a5991905b5b808211156150da57600081556001016150f9565b60008083601f84011261511e578081fd5b5081356001600160401b03811115615134578182fd5b6020830191508360208260051b850101111561514f57600080fd5b9250929050565b600060208284031215615167578081fd5b8135613b81816159d7565b600060208284031215615183578081fd5b8151613b81816159d7565b600080600080600080600080610100898b0312156151aa578384fd5b88356151b5816159d7565b975060208901356151c5816159d7565b965060408901356151d5816159d7565b955060608901356151e5816159d7565b945060808901356151f581615a0c565b935060a0890135615205816159fa565b925060c0890135615215816159d7565b8092505060e089013590509295985092959890939650565b6000806040838503121561523f578182fd5b823561524a816159d7565b915060208301356001600160401b0380821115615265578283fd5b818501915085601f830112615278578283fd5b81358181111561528a5761528a6159c1565b604051601f8201601f19908116603f011681019083821181831017156152b2576152b26159c1565b816040528281528860208487010111156152ca578586fd5b82602086016020830137856020848301015280955050505050509250929050565b600080604083850312156152fd578182fd5b8235615308816159d7565b946020939093013593505050565b60008060208385031215615328578182fd5b82356001600160401b0381111561533d578283fd5b6153498582860161510d565b90969095509350505050565b6000806000806040858703121561536a578384fd5b84356001600160401b0380821115615380578586fd5b61538c8883890161510d565b909650945060208701359150808211156153a4578384fd5b506153b18782880161510d565b95989497509550505050565b60008060008060008060008060c0898b0312156153d8578182fd5b88356001600160401b03808211156153ee578384fd5b6153fa8c838d0161510d565b909a50985060208b0135975060408b013591506001600160f81b0382168214615421578384fd5b90955060608a0135945060808a01359061543a826159ec565b90935060a08a0135908082111561544f578384fd5b5061545c8b828c0161510d565b999c989b5096995094979396929594505050565b600080600060408486031215615484578081fd5b83356001600160401b038082111561549a578283fd5b818601915086601f8301126154ad578283fd5b8135818111156154bb578384fd5b8760208260061b85010111156154cf578384fd5b602092830195509350508401356154e5816159ec565b809150509250925092565b600060208284031215615501578081fd5b8135613b81816159ec565b60006020828403121561551d578081fd5b8151613b81816159ec565b600060208284031215615539578081fd5b5035919050565b600060208284031215615551578081fd5b81516001600160c01b0381168114613b81578182fd5b60008060408385031215615579578182fd5b50508035926020909101359150565b6000806000806080858703121561559d578182fd5b505082516020840151604085015160609095015191969095509092509050565b600080600080600060a086880312156155d4578283fd5b855194506020860151935060408601516155ed81615a0c565b60608701519093506155fe81615a0c565b608087015190925061560f816159ec565b809150509295509295909350565b6000806040838503121561562f578182fd5b82359150602083013561564181615a0c565b809150509250929050565b60006020828403121561565d578081fd5b8135613b81816159fa565b600060208284031215615679578081fd5b8151613b81816159fa565b60008251615696818460208701615944565b9190910192915050565b6001600160a01b0393909316835260208301919091521515604082015260600190565b60208152600082518060208401526156e2816040850160208701615944565b601f01601f19169190910160400192915050565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b19195b1959d85d1958d85b1b60a21b606082015260800190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b6163746976652070726f787960a01b606082015260800190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b60006001600160801b038083168185168083038211156157fe576157fe6159ab565b01949350505050565b60006001600160f81b038281168482168083038211156157fe576157fe6159ab565b6000821982111561583c5761583c6159ab565b500190565b60006001600160401b038083168185168083038211156157fe576157fe6159ab565b60008261587e57634e487b7160e01b81526012600452602481fd5b500490565b600081600019048311821515161561589d5761589d6159ab565b500290565b60006001600160781b03838116908316818110156158c2576158c26159ab565b039392505050565b60006001600160f81b03838116908316818110156158c2576158c26159ab565b6000828210156158fc576158fc6159ab565b500390565b60006001600160401b03838116908316818110156158c2576158c26159ab565b600060ff821660ff84168082101561593b5761593b6159ab565b90039392505050565b60005b8381101561595f578181015183820152602001615947565b83811115612b515750506000910152565b6000600019821415615984576159846159ab565b5060010190565b600060ff821660ff8114156159a2576159a26159ab565b60010192915050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610a5957600080fd5b8015158114610a5957600080fd5b63ffffffff81168114610a5957600080fd5b6001600160401b0381168114610a5957600080fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a164736f6c6343000804000a
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.