Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x60808060 | 19497226 | 193 days ago | IN | 0 ETH | 0.03472356 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
ChiLocking
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 1000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import "../interfaces/IChiLocking.sol"; import "../library/ExternalContractAddresses.sol"; /// @title Contract for locking CHI tokens /// @notice This contract holds CHI tokens that are locked /// @notice Locking of reward distribution of CHI tokens is done per epoch contract ChiLocking is IChiLocking, OwnableUpgradeable { using SafeERC20 for IERC20; uint256 public constant MAX_LOCK_DURATION = 208; // 4 years in weeks IERC20 public constant stETH = IERC20(ExternalContractAddresses.stETH); IERC20 public chi; address public rewardController; uint256 public currentEpoch; uint256 public totalLockedChi; uint256 public totalUnlockedChi; uint256 public totalLockedShares; uint256 public totalVotingPower; uint256 public sumOfLockedDurations; uint256 public numberOfLockedPositions; uint256 public addVotingPowerInEpoch; uint256 public sumShareDurationProduct; uint256 public addedAmountInEpoch; mapping(address account => bool status) public chiLockers; mapping(address account => LockingData) public locks; mapping(uint256 id => EpochData) public epochs; modifier onlyRewardController() { if (msg.sender != rewardController) { revert NotRewardController(); } _; } modifier onlyChiLockers() { if (!chiLockers[msg.sender]) { revert NotChiLocker(); } _; } function initialize(IERC20 _chi, address _chiStaking) external initializer { __Ownable_init(); chiLockers[_chiStaking] = true; chi = _chi; currentEpoch = 1; } /// @inheritdoc IChiLocking function setUscStaking(address _uscStaking) external onlyOwner { chiLockers[_uscStaking] = true; emit SetUscStaking(_uscStaking); } /// @inheritdoc IChiLocking function setRewardController(address _rewardController) external onlyOwner { rewardController = _rewardController; emit SetRewardController(_rewardController); } /// @inheritdoc IChiLocking function setChiLocker(address contractAddress, bool toSet) external onlyOwner { chiLockers[contractAddress] = toSet; emit SetChiLocker(contractAddress, toSet); } /// @inheritdoc IChiLocking function getLockedPosition(address account, uint256 pos) external view returns (LockedPosition memory) { return locks[account].positions[pos]; } function getAllLockedPositions(address account) external view returns (AllLockedPositionsOutput[] memory out) { LockingData storage lockData = locks[account]; out = new AllLockedPositionsOutput[](lockData.positions.length); for (uint256 i = 0; i < lockData.positions.length; i++) { LockedPosition storage position = lockData.positions[i]; out[i].position = position; out[i].votingPower = _getCurrentVotingPowerForPosition(position); out[i].stETHreward = _getUnclaimedStETHPositionAmount(lockData.positions[i], lockData.lastUpdatedEpoch); out[i].totalAccumulatedChi = _totalAccumulatedAtEpoch(currentEpoch, lockData.positions[i]); out[i].totalChiRewards = (out[i].totalAccumulatedChi > position.amount) ? out[i].totalAccumulatedChi - position.amount : 0; } } /// @inheritdoc IChiLocking function getStakedChi() public view returns (uint256) { return totalLockedChi + totalUnlockedChi; } /// @inheritdoc IChiLocking function getLockedChi() public view returns (uint256) { return totalLockedChi; } /// @inheritdoc IChiLocking function getTotalVotingPower() external view returns (uint256) { return totalVotingPower; } /// @inheritdoc IChiLocking function availableChiWithdraw(address account) public view returns (uint256 availableTotal) { LockingData storage lockData = locks[account]; for (uint256 i = 0; i < lockData.positions.length; i++) { availableTotal += _availableToWithdrawFromPosition(lockData.positions[i]); } } /// @inheritdoc IChiLocking function lockChi(address account, uint256 amount, uint256 duration) external onlyChiLockers { if (amount == 0) { revert ZeroAmount(); } uint256 shares = _getNumberOfShares(amount); duration++; EpochData storage currentEpochData = epochs[currentEpoch]; EpochData storage afterEndEpoch = epochs[currentEpoch + duration]; locks[account].positions.push( LockedPosition({ amount: amount, startEpoch: currentEpoch, duration: duration, shares: shares, withdrawnChiAmount: 0 }) ); totalLockedChi += amount; totalLockedShares += shares; currentEpochData.lockedSharesInEpoch += shares; currentEpochData.totalLockedChiInEpoch += amount; afterEndEpoch.sharesToUnlock += shares; totalVotingPower += Math.mulDiv(amount, duration, MAX_LOCK_DURATION); sumShareDurationProduct += Math.mulDiv(shares, duration, MAX_LOCK_DURATION); sumOfLockedDurations += duration + 1; numberOfLockedPositions += 1; afterEndEpoch.numberOfEndingPositions += 1; emit LockChi(account, amount, shares, currentEpoch, currentEpoch + duration); } /// @inheritdoc IChiLocking function updateEpoch(uint256 chiEmissions, uint256 stETHrewards) external onlyRewardController { EpochData storage epoch = epochs[currentEpoch]; uint256 stETHrewardsForLocked; uint256 stakedChi = getStakedChi(); if (stakedChi != 0) { stETHrewardsForLocked = Math.mulDiv(stETHrewards, totalLockedChi, stakedChi); } uint256 stETHrewardsForUnlocked = stETHrewards - stETHrewardsForLocked; uint256 stEthPerLockedShare; if (epoch.lockedSharesInEpoch != 0) { stEthPerLockedShare = Math.mulDiv(stETHrewardsForLocked, 1e18, epoch.lockedSharesInEpoch); } epoch.cumulativeStETHPerLockedShare = epochs[currentEpoch - 1].cumulativeStETHPerLockedShare + stEthPerLockedShare; uint256 stEthPerUnlocked; if (totalUnlockedChi != 0) { stEthPerUnlocked = Math.mulDiv(stETHrewardsForUnlocked, 1e18, totalUnlockedChi); } epoch.cumulativeStETHPerUnlocked = epochs[currentEpoch - 1].cumulativeStETHPerUnlocked + stEthPerUnlocked; totalVotingPower -= totalLockedChi / MAX_LOCK_DURATION; sumShareDurationProduct -= totalLockedShares / MAX_LOCK_DURATION; if (totalLockedShares != 0) { totalVotingPower += Math.mulDiv(chiEmissions, sumShareDurationProduct, totalLockedShares); } totalLockedChi += chiEmissions; epoch.totalLockedChiInEpoch += chiEmissions; EpochData storage nextEpoch = epochs[currentEpoch + 1]; uint256 amountToUnlock; if (totalLockedShares != 0) { amountToUnlock = Math.mulDiv(epoch.totalLockedChiInEpoch, nextEpoch.sharesToUnlock, epoch.lockedSharesInEpoch); } totalLockedChi = totalLockedChi - amountToUnlock; totalUnlockedChi = totalUnlockedChi + amountToUnlock; totalLockedShares -= nextEpoch.sharesToUnlock; nextEpoch.lockedSharesInEpoch = totalLockedShares; nextEpoch.totalLockedChiInEpoch = totalLockedChi; currentEpoch++; emit UpdateEpoch(currentEpoch - 1, totalLockedChi, chiEmissions, stETHrewards, stEthPerLockedShare); } /// @inheritdoc IChiLocking function claimStETH(address account) external onlyRewardController returns (uint256 amount) { _updateUnclaimedStETH(account); amount = locks[account].unclaimedStETH; locks[account].unclaimedStETH = 0; emit ClaimStETH(account, amount); } /// @inheritdoc IChiLocking function withdrawChiFromAccount(address account, uint256 amount) public onlyChiLockers { withdrawChiFromAccountToAddress(account, account, amount); } /// @inheritdoc IChiLocking function withdrawChiFromAccountToAddress(address account, address toAddress, uint256 amount) public onlyChiLockers { if (amount == 0) { return; } _updateUnclaimedStETH(account); uint256 toWithdraw = amount; LockingData storage lockData = locks[account]; uint256 pos = 0; while (toWithdraw > 0) { if (pos >= lockData.positions.length) { revert UnavailableWithdrawAmount(amount); } LockedPosition storage position = lockData.positions[pos]; if (currentEpoch < position.startEpoch + position.duration) { pos++; } else { uint256 availableToWithdraw = _availableToWithdrawFromPosition(position); if (availableToWithdraw > toWithdraw) { position.withdrawnChiAmount += toWithdraw; toWithdraw = 0; } else { position.withdrawnChiAmount += toWithdraw; toWithdraw -= availableToWithdraw; _removePosition(lockData, pos); } } } totalUnlockedChi -= amount; chi.safeTransfer(toAddress, amount); emit WithdrawChiFromAccount(account, toAddress, amount); } /// @inheritdoc IChiLocking function unclaimedStETHAmount(address account) public view returns (uint256 totalAmount) { LockingData storage lockData = locks[account]; totalAmount = lockData.unclaimedStETH; if (lockData.lastUpdatedEpoch == currentEpoch) return totalAmount; for (uint256 i = 0; i < lockData.positions.length; i++) { totalAmount += _getUnclaimedStETHPositionAmount(lockData.positions[i], lockData.lastUpdatedEpoch); } } /// @inheritdoc IChiLocking function getVotingPower(address account) public view returns (uint256) { uint256 votingPower = 0; LockingData storage lockData = locks[account]; for (uint256 i = 0; i < lockData.positions.length; i++) { votingPower += _getCurrentVotingPowerForPosition(lockData.positions[i]); } return votingPower; } function _getNumberOfShares(uint256 chiAmount) internal view returns (uint256) { if (totalLockedChi == 0) return chiAmount; return Math.mulDiv(chiAmount, totalLockedShares, totalLockedChi); } function _updateUnclaimedStETH(address account) internal { locks[account].unclaimedStETH = unclaimedStETHAmount(account); locks[account].lastUpdatedEpoch = currentEpoch; } function _getUnclaimedStETHPositionAmount( LockedPosition storage position, uint256 lastUpdated ) internal view returns (uint256 unclaimedAmount) { if (lastUpdated == currentEpoch) return 0; uint256 fromEpoch = lastUpdated < position.startEpoch ? position.startEpoch : lastUpdated; uint256 toEpoch = currentEpoch - 1; uint256 lockEndsInEpoch = position.startEpoch + position.duration - 1; if (fromEpoch <= lockEndsInEpoch) { if (toEpoch <= lockEndsInEpoch) { unclaimedAmount += _unclaiemdStETHDuringLocked(position, fromEpoch, toEpoch); return unclaimedAmount; } else { unclaimedAmount += _unclaiemdStETHDuringLocked(position, fromEpoch, lockEndsInEpoch); } fromEpoch = lockEndsInEpoch + 1; } unclaimedAmount += _unclaiemdStETHAfterLocked(position, fromEpoch, toEpoch); } function _unclaiemdStETHDuringLocked( LockedPosition storage position, uint256 fromEpoch, uint256 toEpoch ) internal view returns (uint256) { uint256 rewardPerShare = epochs[toEpoch].cumulativeStETHPerLockedShare - epochs[fromEpoch - 1].cumulativeStETHPerLockedShare; return Math.mulDiv(rewardPerShare, position.shares, 1e18); } function _unclaiemdStETHAfterLocked( LockedPosition storage position, uint256 fromEpoch, uint256 toEpoch ) internal view returns (uint256) { uint256 unlockedChiAmount = _availableToWithdrawFromPosition(position); uint256 rewardPerChi = epochs[toEpoch].cumulativeStETHPerUnlocked - epochs[fromEpoch - 1].cumulativeStETHPerUnlocked; return Math.mulDiv(rewardPerChi, unlockedChiAmount, 1e18); } function _getCurrentVotingPowerForPosition(LockedPosition storage position) internal view returns (uint256) { if (currentEpoch >= position.startEpoch + position.duration || currentEpoch < position.startEpoch) return 0; uint256 epochsUntilEnd = position.startEpoch + position.duration - currentEpoch; if (currentEpoch == position.startEpoch) { return Math.mulDiv(position.amount, epochsUntilEnd, MAX_LOCK_DURATION); } else { return Math.mulDiv(_totalAccumulatedAtEpoch(currentEpoch - 1, position), epochsUntilEnd, MAX_LOCK_DURATION); } } function _availableToWithdrawFromPosition(LockedPosition storage position) internal view returns (uint256) { uint256 endLockingEpoch = position.startEpoch + position.duration - 1; if (currentEpoch <= endLockingEpoch) return 0; return _totalAccumulatedAtEpoch(endLockingEpoch, position) - position.withdrawnChiAmount; } function _totalAccumulatedAtEpoch(uint256 epochNum, LockedPosition storage position) internal view returns (uint256) { uint256 endEpoch = position.startEpoch + position.duration - 1; if (endEpoch < epochNum) epochNum = endEpoch; EpochData storage epoch = epochs[epochNum]; return Math.mulDiv(position.shares, epoch.totalLockedChiInEpoch, epoch.lockedSharesInEpoch); } function _removePosition(LockingData storage lockData, uint256 pos) internal { lockData.positions[pos] = lockData.positions[lockData.positions.length - 1]; lockData.positions.pop(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal onlyInitializing { __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @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 proxied contracts do not make use of 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. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * 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. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized != type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @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, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or 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 { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // 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 /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) 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 onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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 `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 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(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. 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"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @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). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // 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 cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @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, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or 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 { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // 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 /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IChiLocking { struct LockedPosition { uint256 amount; uint256 startEpoch; uint256 duration; // in epochs uint256 shares; uint256 withdrawnChiAmount; } struct LockingData { uint256 lastUpdatedEpoch; uint256 unclaimedStETH; LockedPosition[] positions; } struct AllLockedPositionsOutput { LockedPosition position; uint256 votingPower; uint256 stETHreward; uint256 totalAccumulatedChi; uint256 totalChiRewards; } struct EpochData { uint256 lockedSharesInEpoch; uint256 totalLockedChiInEpoch; uint256 sharesToUnlock; uint256 cumulativeStETHPerLockedShare; uint256 cumulativeStETHPerUnlocked; uint256 numberOfEndingPositions; } event SetUscStaking(address indexed uscStaking); event SetRewardController(address indexed rewardController); event SetChiLocker(address indexed chiLocker, bool indexed status); event LockChi(address indexed account, uint256 amount, uint256 shares, uint256 startEpoch, uint256 endEpoch); event UpdateEpoch( uint256 indexed epoch, uint256 totalLockedChi, uint256 chiEmissions, uint256 stETHrewards, uint256 stEthPerLockedShare ); event ClaimStETH(address indexed account, uint256 amount); event WithdrawChiFromAccount(address indexed account, address indexed toAddress, uint256 amount); error ZeroAmount(); error NotRewardController(); error NotChiLocker(); error UnavailableWithdrawAmount(uint256 amount); /// @notice Sets address of uscStaking contract /// @param _uscStaking Address of uscStaking contract function setUscStaking(address _uscStaking) external; /// @notice Sets address of rewardController contract /// @param _rewardController Address of rewardController contract function setRewardController(address _rewardController) external; /// @notice Sets address of contract who can call lock function /// @param contractAddress Address of contract who calles lock function, chiStaking currently /// @param toSet true if contract can call lock function, false otherwise function setChiLocker(address contractAddress, bool toSet) external; /// @notice Gets locked position for given account and position index /// @param account Account to get locked position for /// @param pos Index of locked position /// @return position Locked position function getLockedPosition(address account, uint256 pos) external view returns (LockedPosition memory position); /// @notice Gets all locked position for given account /// @param account Account to get locked positions /// @return out Array of locked positions function getAllLockedPositions(address account) external view returns (AllLockedPositionsOutput[] memory out); /// @notice Gets total staked chi amount, locked amount is also considered staked /// @return stakedChi Total staked chi amount function getStakedChi() external view returns (uint256 stakedChi); /// @notice Gets total locked chi amount /// @return lockedChi Total locked chi amount function getLockedChi() external view returns (uint256 lockedChi); /// @notice Gets total voting power /// @return totalVotingPower Total voting power function getTotalVotingPower() external view returns (uint256 totalVotingPower); /// @notice Gets total chi amount that is available to withdraw for given account /// @param account Account to get available chi amount for /// @return availableTotal Total amount of chi that is available to withdraw function availableChiWithdraw(address account) external view returns (uint256 availableTotal); /// @notice Locks given amount of chi for given account for given duration /// @param account Account to lock chi for /// @param amount Amount of chi to lock /// @param duration Duration of locking in epochs /// @custom:usage This function should be called from chiStaking and uscStaking contracts in purpose of locking chi function lockChi(address account, uint256 amount, uint256 duration) external; /// @notice Updates epoch data /// @param chiEmissions Amount of chi incentives for chi lockers that is emitted in current epoch /// @param stETHrewards Amount of stETH rewards for chi lockers that is emitted in current epoch /// @custom:usage This function should be called from rewardController contract in purpose of updating epoch data function updateEpoch(uint256 chiEmissions, uint256 stETHrewards) external; /// @notice Claims stETH rewards for given account /// @notice This contract does not send stETH rewards nor holds them, reserveHolder does that /// @notice This contract only calculates and updates unclaimed stETH amount for given account /// @param account Account to claim stETH rewards for /// @return amount Amount of stETH rewards that user can claim /// @custom:usage This function should be called from rewardController contract in purpose of claiming stETH rewards function claimStETH(address account) external returns (uint256 amount); /// @notice Withdraws given amount of unlocked chi tokens for given account, sends to account by default /// @notice This contract hold CHI tokens and inside this function sends them back to user /// @param account Account to withdraw CHI for /// @param amount Amount of CHI tokens to withdraw /// @custom:usage This function should be called from chiStaking contract in purpose of withdrawing CHI tokens function withdrawChiFromAccount(address account, uint256 amount) external; /// @notice Withdraws given amount of unlocked chi tokens for given account, sends to account by default /// @notice This contract hold CHI tokens and inside this function sends them back to user /// @param account Account to withdraw CHI for /// @param toAddress Address to which to send tokens /// @param amount Amount of CHI tokens to withdraw /// @custom:usage This function should be called from chiStaking contract in purpose of withdrawing CHI tokens function withdrawChiFromAccountToAddress(address account, address toAddress, uint256 amount) external; /// @notice Calculates and returns unclaimed stETH amount for given account /// @param account Account to calculate unclaimed stETH amount for /// @return totalAmount Total amount of unclaimed stETH for given account function unclaimedStETHAmount(address account) external view returns (uint256 totalAmount); /// @notice Calculates and returns voting power for given account /// @param account Account to calculate voting power for /// @return votingPower Voting power for given account function getVotingPower(address account) external view returns (uint256 votingPower); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @notice external contract addresses on Ethereum Mainnet library ExternalContractAddresses { address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address public constant stETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; address public constant UNI_V2_SWAP_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; address public constant UNI_V2_POOL_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; address public constant ETH_USD_CHAINLINK_FEED = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; address public constant STETH_USD_CHAINLINK_FEED = 0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8; address public constant CURVE_ETH_STETH_POOL = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; }
{ "optimizer": { "enabled": true, "runs": 1000 }, "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"NotChiLocker","type":"error"},{"inputs":[],"name":"NotRewardController","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UnavailableWithdrawAmount","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimStETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endEpoch","type":"uint256"}],"name":"LockChi","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"chiLocker","type":"address"},{"indexed":true,"internalType":"bool","name":"status","type":"bool"}],"name":"SetChiLocker","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardController","type":"address"}],"name":"SetRewardController","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"uscStaking","type":"address"}],"name":"SetUscStaking","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalLockedChi","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"chiEmissions","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stETHrewards","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stEthPerLockedShare","type":"uint256"}],"name":"UpdateEpoch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawChiFromAccount","type":"event"},{"inputs":[],"name":"MAX_LOCK_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"addVotingPowerInEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"addedAmountInEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"availableChiWithdraw","outputs":[{"internalType":"uint256","name":"availableTotal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chi","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"chiLockers","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"claimStETH","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"epochs","outputs":[{"internalType":"uint256","name":"lockedSharesInEpoch","type":"uint256"},{"internalType":"uint256","name":"totalLockedChiInEpoch","type":"uint256"},{"internalType":"uint256","name":"sharesToUnlock","type":"uint256"},{"internalType":"uint256","name":"cumulativeStETHPerLockedShare","type":"uint256"},{"internalType":"uint256","name":"cumulativeStETHPerUnlocked","type":"uint256"},{"internalType":"uint256","name":"numberOfEndingPositions","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAllLockedPositions","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startEpoch","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"withdrawnChiAmount","type":"uint256"}],"internalType":"struct IChiLocking.LockedPosition","name":"position","type":"tuple"},{"internalType":"uint256","name":"votingPower","type":"uint256"},{"internalType":"uint256","name":"stETHreward","type":"uint256"},{"internalType":"uint256","name":"totalAccumulatedChi","type":"uint256"},{"internalType":"uint256","name":"totalChiRewards","type":"uint256"}],"internalType":"struct IChiLocking.AllLockedPositionsOutput[]","name":"out","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLockedChi","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"pos","type":"uint256"}],"name":"getLockedPosition","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startEpoch","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"withdrawnChiAmount","type":"uint256"}],"internalType":"struct IChiLocking.LockedPosition","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakedChi","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalVotingPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotingPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_chi","type":"address"},{"internalType":"address","name":"_chiStaking","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"lockChi","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"locks","outputs":[{"internalType":"uint256","name":"lastUpdatedEpoch","type":"uint256"},{"internalType":"uint256","name":"unclaimedStETH","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfLockedPositions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardController","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"bool","name":"toSet","type":"bool"}],"name":"setChiLocker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardController","type":"address"}],"name":"setRewardController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_uscStaking","type":"address"}],"name":"setUscStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stETH","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sumOfLockedDurations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sumShareDurationProduct","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLockedChi","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLockedShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUnlockedChi","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalVotingPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unclaimedStETHAmount","outputs":[{"internalType":"uint256","name":"totalAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chiEmissions","type":"uint256"},{"internalType":"uint256","name":"stETHrewards","type":"uint256"}],"name":"updateEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawChiFromAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"toAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawChiFromAccountToAddress","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6080806040523461001657612021908161001b8239f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f9160e08335811c90816306fe3cc81461129057816308e9cb811461123257816311acc1a714610c3657816326db209c146111ab5781633dc85ee01461118b578163441b6bfc14610c76578163485cc95514610fef5781634f1bfc9e14610fd257816353fdfab014610d5057816358b8f73a14610d305781635d4ed70514610cd85781635de9a13714610c965781635f2bcdbd14610c765781636222be5c14610c56578163671b379314610c36578163715018a614610bce57816374fe237014610b915781637667180814610b715781637c99396114610afd57816381c39bec14610add5781638cc5ce9914610ab45781638da5cb5b14610a8b5781638f846f5614610a6b5781639c3e34a21461081c57508063a631909014610582578063b01d556c14610563578063bb4d4436146104f8578063be596d3a1461046b578063bf230f7f146103f4578063c1fe3e48146103c5578063c67039b41461035c578063c6b61e4c146102ff578063c89785a3146102db578063c92aecc4146102b3578063e8c3a80814610286578063f0bb08a9146102635763f2fde38b146101b8575f80fd5b3461025f57602036600319011261025f576101d161130f565b916101da61133f565b6001600160a01b038316156101f657836101f384611397565b80f35b906020608492519162461bcd60e51b8352820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b8280fd5b505034610282578160031936011261028257602090606f549051908152f35b5080fd5b505034610282576020366003190112610282576020906102ac6102a761130f565b611caa565b9051908152f35b5050346102825781600319360112610282576020906001600160a01b03606554169051908152f35b5050346102825781600319360112610282576020906102ac6068546069549061159c565b50903461025f57602036600319011261025f578060c09383358152607360205220805492600182015492600283015490600560038501549385015494015494815196875260208701528501526060840152608083015260a0820152f35b505034610282576020366003190112610282576001600160a01b0361037f61130f565b61038761133f565b168083526071602052908220805460ff191660011790557fccfcf0bb99345595032f97ec9cad597ffd38164e230056a3291ccb6ccb5bf34b8280a280f35b5050346102825781600319360112610282576020905173ae7ab96520de3a18e5e111b5eaab095312d7fe848152f35b5050346102825760203660031901126102825761040f61130f565b906001600160a01b03839216835260726020526002818420019283545b80821061043d576020848451908152f35b909261045f6104659161045961045387896114d8565b50611f51565b9061159c565b93611559565b9061042c565b509190346102825760203660031901126102825761048761130f565b6001600160a01b0390816066541633036104e957602094506104a881611d2b565b168083526072845260018284200192835493557fec968375d87c8bc8b1fb440238b4e7bc0babfbbdba5dc249a1e08cb5e2a19f22848351858152a251908152f35b84835163229f674560e21b8152fd5b5050346102825760203660031901126102825761051361130f565b906001600160a01b03839216835260726020526002818420019283545b808210610541576020848451908152f35b909261045f61055d9161045961055787896114d8565b50611ed8565b90610530565b5050346102825781600319360112610282576020906069549051908152f35b508290346102825760603660031901126102825761059e61130f565b90602490813590338552607160205260ff86862054161561080e5781156107e7576105c882611d12565b926105d4604435611559565b91606754918288526073602052888820966105ef858561159c565b895260736020526001600160a01b038a8a20911697888a52607260205260028b8b2001948a8c51916106208361145c565b898352602083019081528d830189815260608401918c8352608085019384528954680100000000000000008110156107d4576106639060019b8c820181556114d8565b9590956107c25751855551848a0155516002840155516003830155519085015560685485919061069490899061159c565b6068556106a389606a5461159c565b606a556106b189825461159c565b8155016106bf87825461159c565b9055600281016106d088825461159c565b90556106e76106df8688611699565b606b5461159c565b606b556106ff6106f78689611699565b606f5461159c565b606f558385018086116107b05761071890606c5461159c565b606c55606d548481018091116107b05790600591606d550191825493840180941161079f575050917f4bb50af5f408a0e5f29c38fa94790fef78a68256834c4f581360a6eb481932ae959697916107999355610777606754918261159c565b9151948594859094939260609260808301968352602083015260408201520152565b0390a280f35b60118991634e487b7160e01b835252fd5b828a601186634e487b7160e01b835252fd5b50878f808b634e487b7160e01b825252fd5b50878f60418b634e487b7160e01b835252fd5b85517f1f2a2005000000000000000000000000000000000000000000000000000000008152fd5b8551636a26af9160e11b8152fd5b848491346102825760208060031936011261025f579290916001600160a01b0361084461130f565b1681526072845281812091600283019283549061086082611541565b9461086d8451968761148c565b828652601f1961087c84611541565b01855b818110610a1f575050849154606754925b84811061093e575050505050805194859481860192828752855180945282818801960194915b8483106108c35787870388f35b9193958597509261012060019294608083985161090983825160808091805184526020810151602085015260408101516040850152606081015160608501520151910152565b8481015160a08401528781015160c08401526060810151878401520151610100820152019701930190918796959394926108b6565b8080610954610a0993869d9a999d9c9b9c6114d8565b5061095f828d61157b565b5161096982611505565b90528b8a6109808461097a85611ed8565b9361157b565b5101528b8d61099d8461097a89610997838d6114d8565b50611d56565b510152816109b56109ae82896114d8565b5089611f98565b8d6109c3606093849261157b565b5101528c816109d2858361157b565b51015192548093115f14610a15576109f5936109ed9161157b565b51015161158f565b6080610a01838d61157b565b510152611559565b98949598979697610890565b50505050866109f5565b98859981979699989995949551610a358161145c565b610a3d6114ae565b81528783820152878c82015287606082015287608082015282828c010152019894959897969793929361087f565b50505034610282578160031936011261028257602090606d549051908152f35b505050346102825781600319360112610282576020906001600160a01b03603354169051908152f35b505050346102825781600319360112610282576020906001600160a01b03606654169051908152f35b50505034610282578160031936011261028257602090606a549051908152f35b8434610b6e576020366003190112610b6e576001600160a01b03610b1f61130f565b610b2761133f565b168073ffffffffffffffffffffffffffffffffffffffff1960665416176066557f06369d9752ae054ed57067b8fc5085615cbae5c52f23cfa482225d4c85cf15778280a280f35b80fd5b505050346102825781600319360112610282576020906067549051908152f35b505050346102825760203660031901126102825760ff816020936001600160a01b03610bbb61130f565b1681526071855220541690519015158152f35b8434610b6e5780600319360112610b6e57610be761133f565b806001600160a01b0360335473ffffffffffffffffffffffffffffffffffffffff198116603355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50505034610282578160031936011261028257602090606b549051908152f35b50505034610282578160031936011261028257602090606c549051908152f35b505050346102825781600319360112610282576020906068549051908152f35b505050346102825760203660031901126102825780916001600160a01b03610cbc61130f565b1681526072602052206001815491015482519182526020820152f35b8285853461028257606036600319011261028257610cf461130f565b90610cfd611329565b90338452607160205260ff818520541615610d215750906101f391604435916117e5565b849051636a26af9160e11b8152fd5b50505034610282578160031936011261028257602090606e549051908152f35b828585346102825780600319360112610282578235602435916001600160a01b03606654163303610fc35760675492838552607360205281852091859260688054610d9d6069548261159c565b9081610faf575b5050610db0858561158f565b8254899681610f9c575b505f1998808a0191508111610f5f5789526073602052610de0866003868c20015461159c565b6003840155606954899181610f8b575b5050606754888101908111610f5f5790610e16918a5260736020528a858b20015461159c565b89830155610e58868254610e2f60d08204606b5461158f565b80606b55606a5490610e4660d08304606f5461158f565b80606f5582610f72575b50505061159c565b81556001820191610e6a87845461159c565b80935560675460018101809111610f5f576001969594939291610ec6918b526073602052610eb8858c20948c90606a549384610f46575b5050610eae81865461158f565b855560695461159c565b60695560028401549061158f565b80606a558255549485910155610edd606754611559565b95866067558601958611610f3357519283526020830193909352604082019290925260608101919091527f0247f0b2d9e24bc21c30f72194dced894d774fadfd33449450fcc3ff7d9f5c89908060808101610799565b60248760118a634e487b7160e01b835252fd5b610f5892506002880154905491611771565b8e80610ea1565b60248a60118d634e487b7160e01b835252fd5b610f80926104599186611771565b606b558c8080610e50565b610f9592506115c7565b8a80610df0565b610fa79297506115c7565b948a80610dba565b610fbb92965085611771565b938980610da4565b84905163229f674560e21b8152fd5b505050346102825781600319360112610282576020905160d08152f35b50503461025f578160031936011261025f5780356001600160a01b038082168092036111875761101d611329565b9085549160ff8360081c16159485809661117a575b8015611163575b156110fa575060ff19838116600117885592856110e9575b5061106b60ff885460081c16611066816113eb565b6113eb565b61107433611397565b168552607160205260018486209182541617905573ffffffffffffffffffffffffffffffffffffffff19606554161760655560016067556110b3575080f35b60207f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989161ff001984541684555160018152a180f35b61ffff19166101011787555f611051565b608490602088519162461bcd60e51b8352820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b50303b1580156110395750600160ff851614611039565b50600160ff851610611032565b8480fd5b505050346102825781600319360112610282576020906070549051908152f35b505050346102825780600319360112610282576112306111ff6111f960a0946001600160a01b036111da61130f565b6111e26114ae565b5016815260726020526002856024359220016114d8565b50611505565b9151809260808091805184526020810151602085015260408101516040850152606081015160608501520151910152565bf35b50503461025f578160031936011261025f5761124c61130f565b91338452607160205260ff81852054161561128357338452607160205260ff81852054161561128357836101f360243585806117e5565b51636a26af9160e11b8152fd5b505050346102825780600319360112610282576112ab61130f565b906024359182151580930361130b576001600160a01b03906112cb61133f565b16908184526071602052832060ff1981541660ff84161790557f81c700dd5817989785a30864f6a3d8c9da359f5d7de7f695cd304fdda6daff3f8380a380f35b8380fd5b600435906001600160a01b038216820361132557565b5f80fd5b602435906001600160a01b038216820361132557565b6001600160a01b0360335416330361135357565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b603354906001600160a01b03809116918273ffffffffffffffffffffffffffffffffffffffff19821617603355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b156113f257565b608460405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152fd5b60a0810190811067ffffffffffffffff82111761147857604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff82111761147857604052565b604051906114bb8261145c565b5f6080838281528260208201528260408201528260608201520152565b80548210156114f1575f52600560205f20910201905f90565b634e487b7160e01b5f52603260045260245ffd5b906040516115128161145c565b608060048294805484526001810154602085015260028101546040850152600381015460608501520154910152565b67ffffffffffffffff81116114785760051b60200190565b5f1981146115675760010190565b634e487b7160e01b5f52601160045260245ffd5b80518210156114f15760209160051b010190565b9190820391821161156757565b9190820180921161156757565b81156115b3570490565b634e487b7160e01b5f52601260045260245ffd5b90670de0b6b3a7640000905f19828409928281029283808610950394808603951461168957848311156116455782910960018219018216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b606460405162461bcd60e51b815260206004820152601560248201527f4d6174683a206d756c446976206f766572666c6f7700000000000000000000006044820152fd5b50509061169692506115a9565b90565b5f19828209828202918280831092039180830392146116f4578160d01115611645577f4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec59360d0910990828211900360fc1b910360041c170290565b505060d091500490565b905f198183098183029182808310920391808303921461176057670de0b6b3a76400009082821115611645577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac10669940990828211900360ee1b910360121c170290565b5050670de0b6b3a764000091500490565b915f19828409928281029283808610950394808603951461168957848311156116455782910960018219018216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b91908115611bd4576117f683611d2b565b816001600160a01b03809416925f84815260209360728552604092838320908084926002809101915b611a2957505050506118338460695461158f565b606955866065541696835190868201927fa9059cbb000000000000000000000000000000000000000000000000000000008452169788602483015285604483015260448252608082019267ffffffffffffffff9280851084861117611a155760c0810185811085821117611a015787528885527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a082015251859182919082855af1903d156119f2573d9283116119de579061190f9392918651926119028a601f19601f840116018561148c565b83523d868a85013e611bd9565b8051918215918683156119ba575b50505090501561195157519081527fa7ac4a256be44b38d80629efa31e70ad72090fe09fc2d4dcde7f1727dd0190009190a3565b82608491519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b91938180945001031261028257840151908115158203610b6e5750805f808661191d565b602485634e487b7160e01b81526041600452fd5b9061190f939250606091611bd9565b602487634e487b7160e01b81526041600452fd5b602486634e487b7160e01b81526041600452fd5b8154841015611ba457611a3c84836114d8565b50606754600190611a5482840154858501549061159c565b1115611a6c575050611a668394611559565b9361181f565b611a7882959395611f51565b83811115611a9b5750506004611a91910191825461159c565b905584918261181f565b611ab9919295936004809701611ab282825461159c565b905561158f565b9383545f1990818101908111611b9157611ad390866114d8565b50611ade88876114d8565b919091611b7f578084918303611b53575b50505084548015611b40570191611b0683866114d8565b919091611b2e5791818a8895948180955582015582868201558260038201550155835561181f565b60248a8085634e487b7160e01b825252fd5b60248a603185634e487b7160e01b835252fd5b8054835585810154868401558681015487840155600380820154908401558101549101555f8281611aef565b60248b8086634e487b7160e01b825252fd5b60248a601185634e487b7160e01b835252fd5b6024888851907f97a27f060000000000000000000000000000000000000000000000000000000082526004820152fd5b505050565b91929015611c3a5750815115611bed575090565b3b15611bf65790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b825190915015611c4d5750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401525f935b828510611c91575050604492505f838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350611c6f565b6001600160a01b03165f52607260205260405f209060018201549180546067548114611d0e5760025f9201908154915b828410611ce75750505050565b90919294611d00611d0691610459856109978a876114d8565b95611559565b929190611cda565b5050565b6068548015611d2757606a5461169692611771565b5090565b6001600160a01b03611d3c82611caa565b91165f52607260205260405f209060018201556067549055565b6067545f93928492828214611e755760018101549182811015611e6f575081905b815f1993848601958611611e5b576002830154611d939161159c565b848101818111611e475780831115611e07575b505050611db290611f51565b92865260736020526004604087200154918101908111611df35761045992916004604088611dee94611696999a5260736020522001549061158f565b6116fe565b602486634e487b7160e01b81526011600452fd5b91949293509190808611611e2957505050611696949550906104599291611e7b565b95610459611db29397611e3d939686611e7b565b9491905f80611da6565b60248a634e487b7160e01b81526011600452fd5b602489634e487b7160e01b81526011600452fd5b90611d77565b50505050565b5f9283526073602052604083206003015492939290915f198101908111611ec45760039182604087611ebb9461169698995260736020522001549061158f565b910154906116fe565b602485634e487b7160e01b81526011600452fd5b6067546001820154906002830154611ef0818461159c565b8210801590611f48575b611f405781611f0c611f11928561159c565b61158f565b918103611f2357506116969154611699565b5f1981019081116115675761169692611f3b91611f98565b611699565b505050505f90565b50828210611efa565b611f64600182015460028301549061159c565b5f1981019190821161156757816067541115611f92576004611f898261169694611f98565b9101549061158f565b50505f90565b90611fac600182015460028301549061159c565b5f198101929083116115675782816116969410611fe3575b505f526073602052600360405f20910154906001810154905491611771565b90505f611fc456fea2646970667358221220586c3927e2591c1f3aee36fc546829f6692d32bf4e54f5b0202df53e086772df64736f6c63430008140033
Deployed Bytecode
0x60406080815260049081361015610014575f80fd5b5f9160e08335811c90816306fe3cc81461129057816308e9cb811461123257816311acc1a714610c3657816326db209c146111ab5781633dc85ee01461118b578163441b6bfc14610c76578163485cc95514610fef5781634f1bfc9e14610fd257816353fdfab014610d5057816358b8f73a14610d305781635d4ed70514610cd85781635de9a13714610c965781635f2bcdbd14610c765781636222be5c14610c56578163671b379314610c36578163715018a614610bce57816374fe237014610b915781637667180814610b715781637c99396114610afd57816381c39bec14610add5781638cc5ce9914610ab45781638da5cb5b14610a8b5781638f846f5614610a6b5781639c3e34a21461081c57508063a631909014610582578063b01d556c14610563578063bb4d4436146104f8578063be596d3a1461046b578063bf230f7f146103f4578063c1fe3e48146103c5578063c67039b41461035c578063c6b61e4c146102ff578063c89785a3146102db578063c92aecc4146102b3578063e8c3a80814610286578063f0bb08a9146102635763f2fde38b146101b8575f80fd5b3461025f57602036600319011261025f576101d161130f565b916101da61133f565b6001600160a01b038316156101f657836101f384611397565b80f35b906020608492519162461bcd60e51b8352820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b8280fd5b505034610282578160031936011261028257602090606f549051908152f35b5080fd5b505034610282576020366003190112610282576020906102ac6102a761130f565b611caa565b9051908152f35b5050346102825781600319360112610282576020906001600160a01b03606554169051908152f35b5050346102825781600319360112610282576020906102ac6068546069549061159c565b50903461025f57602036600319011261025f578060c09383358152607360205220805492600182015492600283015490600560038501549385015494015494815196875260208701528501526060840152608083015260a0820152f35b505034610282576020366003190112610282576001600160a01b0361037f61130f565b61038761133f565b168083526071602052908220805460ff191660011790557fccfcf0bb99345595032f97ec9cad597ffd38164e230056a3291ccb6ccb5bf34b8280a280f35b5050346102825781600319360112610282576020905173ae7ab96520de3a18e5e111b5eaab095312d7fe848152f35b5050346102825760203660031901126102825761040f61130f565b906001600160a01b03839216835260726020526002818420019283545b80821061043d576020848451908152f35b909261045f6104659161045961045387896114d8565b50611f51565b9061159c565b93611559565b9061042c565b509190346102825760203660031901126102825761048761130f565b6001600160a01b0390816066541633036104e957602094506104a881611d2b565b168083526072845260018284200192835493557fec968375d87c8bc8b1fb440238b4e7bc0babfbbdba5dc249a1e08cb5e2a19f22848351858152a251908152f35b84835163229f674560e21b8152fd5b5050346102825760203660031901126102825761051361130f565b906001600160a01b03839216835260726020526002818420019283545b808210610541576020848451908152f35b909261045f61055d9161045961055787896114d8565b50611ed8565b90610530565b5050346102825781600319360112610282576020906069549051908152f35b508290346102825760603660031901126102825761059e61130f565b90602490813590338552607160205260ff86862054161561080e5781156107e7576105c882611d12565b926105d4604435611559565b91606754918288526073602052888820966105ef858561159c565b895260736020526001600160a01b038a8a20911697888a52607260205260028b8b2001948a8c51916106208361145c565b898352602083019081528d830189815260608401918c8352608085019384528954680100000000000000008110156107d4576106639060019b8c820181556114d8565b9590956107c25751855551848a0155516002840155516003830155519085015560685485919061069490899061159c565b6068556106a389606a5461159c565b606a556106b189825461159c565b8155016106bf87825461159c565b9055600281016106d088825461159c565b90556106e76106df8688611699565b606b5461159c565b606b556106ff6106f78689611699565b606f5461159c565b606f558385018086116107b05761071890606c5461159c565b606c55606d548481018091116107b05790600591606d550191825493840180941161079f575050917f4bb50af5f408a0e5f29c38fa94790fef78a68256834c4f581360a6eb481932ae959697916107999355610777606754918261159c565b9151948594859094939260609260808301968352602083015260408201520152565b0390a280f35b60118991634e487b7160e01b835252fd5b828a601186634e487b7160e01b835252fd5b50878f808b634e487b7160e01b825252fd5b50878f60418b634e487b7160e01b835252fd5b85517f1f2a2005000000000000000000000000000000000000000000000000000000008152fd5b8551636a26af9160e11b8152fd5b848491346102825760208060031936011261025f579290916001600160a01b0361084461130f565b1681526072845281812091600283019283549061086082611541565b9461086d8451968761148c565b828652601f1961087c84611541565b01855b818110610a1f575050849154606754925b84811061093e575050505050805194859481860192828752855180945282818801960194915b8483106108c35787870388f35b9193958597509261012060019294608083985161090983825160808091805184526020810151602085015260408101516040850152606081015160608501520151910152565b8481015160a08401528781015160c08401526060810151878401520151610100820152019701930190918796959394926108b6565b8080610954610a0993869d9a999d9c9b9c6114d8565b5061095f828d61157b565b5161096982611505565b90528b8a6109808461097a85611ed8565b9361157b565b5101528b8d61099d8461097a89610997838d6114d8565b50611d56565b510152816109b56109ae82896114d8565b5089611f98565b8d6109c3606093849261157b565b5101528c816109d2858361157b565b51015192548093115f14610a15576109f5936109ed9161157b565b51015161158f565b6080610a01838d61157b565b510152611559565b98949598979697610890565b50505050866109f5565b98859981979699989995949551610a358161145c565b610a3d6114ae565b81528783820152878c82015287606082015287608082015282828c010152019894959897969793929361087f565b50505034610282578160031936011261028257602090606d549051908152f35b505050346102825781600319360112610282576020906001600160a01b03603354169051908152f35b505050346102825781600319360112610282576020906001600160a01b03606654169051908152f35b50505034610282578160031936011261028257602090606a549051908152f35b8434610b6e576020366003190112610b6e576001600160a01b03610b1f61130f565b610b2761133f565b168073ffffffffffffffffffffffffffffffffffffffff1960665416176066557f06369d9752ae054ed57067b8fc5085615cbae5c52f23cfa482225d4c85cf15778280a280f35b80fd5b505050346102825781600319360112610282576020906067549051908152f35b505050346102825760203660031901126102825760ff816020936001600160a01b03610bbb61130f565b1681526071855220541690519015158152f35b8434610b6e5780600319360112610b6e57610be761133f565b806001600160a01b0360335473ffffffffffffffffffffffffffffffffffffffff198116603355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50505034610282578160031936011261028257602090606b549051908152f35b50505034610282578160031936011261028257602090606c549051908152f35b505050346102825781600319360112610282576020906068549051908152f35b505050346102825760203660031901126102825780916001600160a01b03610cbc61130f565b1681526072602052206001815491015482519182526020820152f35b8285853461028257606036600319011261028257610cf461130f565b90610cfd611329565b90338452607160205260ff818520541615610d215750906101f391604435916117e5565b849051636a26af9160e11b8152fd5b50505034610282578160031936011261028257602090606e549051908152f35b828585346102825780600319360112610282578235602435916001600160a01b03606654163303610fc35760675492838552607360205281852091859260688054610d9d6069548261159c565b9081610faf575b5050610db0858561158f565b8254899681610f9c575b505f1998808a0191508111610f5f5789526073602052610de0866003868c20015461159c565b6003840155606954899181610f8b575b5050606754888101908111610f5f5790610e16918a5260736020528a858b20015461159c565b89830155610e58868254610e2f60d08204606b5461158f565b80606b55606a5490610e4660d08304606f5461158f565b80606f5582610f72575b50505061159c565b81556001820191610e6a87845461159c565b80935560675460018101809111610f5f576001969594939291610ec6918b526073602052610eb8858c20948c90606a549384610f46575b5050610eae81865461158f565b855560695461159c565b60695560028401549061158f565b80606a558255549485910155610edd606754611559565b95866067558601958611610f3357519283526020830193909352604082019290925260608101919091527f0247f0b2d9e24bc21c30f72194dced894d774fadfd33449450fcc3ff7d9f5c89908060808101610799565b60248760118a634e487b7160e01b835252fd5b610f5892506002880154905491611771565b8e80610ea1565b60248a60118d634e487b7160e01b835252fd5b610f80926104599186611771565b606b558c8080610e50565b610f9592506115c7565b8a80610df0565b610fa79297506115c7565b948a80610dba565b610fbb92965085611771565b938980610da4565b84905163229f674560e21b8152fd5b505050346102825781600319360112610282576020905160d08152f35b50503461025f578160031936011261025f5780356001600160a01b038082168092036111875761101d611329565b9085549160ff8360081c16159485809661117a575b8015611163575b156110fa575060ff19838116600117885592856110e9575b5061106b60ff885460081c16611066816113eb565b6113eb565b61107433611397565b168552607160205260018486209182541617905573ffffffffffffffffffffffffffffffffffffffff19606554161760655560016067556110b3575080f35b60207f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989161ff001984541684555160018152a180f35b61ffff19166101011787555f611051565b608490602088519162461bcd60e51b8352820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b50303b1580156110395750600160ff851614611039565b50600160ff851610611032565b8480fd5b505050346102825781600319360112610282576020906070549051908152f35b505050346102825780600319360112610282576112306111ff6111f960a0946001600160a01b036111da61130f565b6111e26114ae565b5016815260726020526002856024359220016114d8565b50611505565b9151809260808091805184526020810151602085015260408101516040850152606081015160608501520151910152565bf35b50503461025f578160031936011261025f5761124c61130f565b91338452607160205260ff81852054161561128357338452607160205260ff81852054161561128357836101f360243585806117e5565b51636a26af9160e11b8152fd5b505050346102825780600319360112610282576112ab61130f565b906024359182151580930361130b576001600160a01b03906112cb61133f565b16908184526071602052832060ff1981541660ff84161790557f81c700dd5817989785a30864f6a3d8c9da359f5d7de7f695cd304fdda6daff3f8380a380f35b8380fd5b600435906001600160a01b038216820361132557565b5f80fd5b602435906001600160a01b038216820361132557565b6001600160a01b0360335416330361135357565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b603354906001600160a01b03809116918273ffffffffffffffffffffffffffffffffffffffff19821617603355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b156113f257565b608460405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152fd5b60a0810190811067ffffffffffffffff82111761147857604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff82111761147857604052565b604051906114bb8261145c565b5f6080838281528260208201528260408201528260608201520152565b80548210156114f1575f52600560205f20910201905f90565b634e487b7160e01b5f52603260045260245ffd5b906040516115128161145c565b608060048294805484526001810154602085015260028101546040850152600381015460608501520154910152565b67ffffffffffffffff81116114785760051b60200190565b5f1981146115675760010190565b634e487b7160e01b5f52601160045260245ffd5b80518210156114f15760209160051b010190565b9190820391821161156757565b9190820180921161156757565b81156115b3570490565b634e487b7160e01b5f52601260045260245ffd5b90670de0b6b3a7640000905f19828409928281029283808610950394808603951461168957848311156116455782910960018219018216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b606460405162461bcd60e51b815260206004820152601560248201527f4d6174683a206d756c446976206f766572666c6f7700000000000000000000006044820152fd5b50509061169692506115a9565b90565b5f19828209828202918280831092039180830392146116f4578160d01115611645577f4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec4ec59360d0910990828211900360fc1b910360041c170290565b505060d091500490565b905f198183098183029182808310920391808303921461176057670de0b6b3a76400009082821115611645577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac10669940990828211900360ee1b910360121c170290565b5050670de0b6b3a764000091500490565b915f19828409928281029283808610950394808603951461168957848311156116455782910960018219018216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b91908115611bd4576117f683611d2b565b816001600160a01b03809416925f84815260209360728552604092838320908084926002809101915b611a2957505050506118338460695461158f565b606955866065541696835190868201927fa9059cbb000000000000000000000000000000000000000000000000000000008452169788602483015285604483015260448252608082019267ffffffffffffffff9280851084861117611a155760c0810185811085821117611a015787528885527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a082015251859182919082855af1903d156119f2573d9283116119de579061190f9392918651926119028a601f19601f840116018561148c565b83523d868a85013e611bd9565b8051918215918683156119ba575b50505090501561195157519081527fa7ac4a256be44b38d80629efa31e70ad72090fe09fc2d4dcde7f1727dd0190009190a3565b82608491519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b91938180945001031261028257840151908115158203610b6e5750805f808661191d565b602485634e487b7160e01b81526041600452fd5b9061190f939250606091611bd9565b602487634e487b7160e01b81526041600452fd5b602486634e487b7160e01b81526041600452fd5b8154841015611ba457611a3c84836114d8565b50606754600190611a5482840154858501549061159c565b1115611a6c575050611a668394611559565b9361181f565b611a7882959395611f51565b83811115611a9b5750506004611a91910191825461159c565b905584918261181f565b611ab9919295936004809701611ab282825461159c565b905561158f565b9383545f1990818101908111611b9157611ad390866114d8565b50611ade88876114d8565b919091611b7f578084918303611b53575b50505084548015611b40570191611b0683866114d8565b919091611b2e5791818a8895948180955582015582868201558260038201550155835561181f565b60248a8085634e487b7160e01b825252fd5b60248a603185634e487b7160e01b835252fd5b8054835585810154868401558681015487840155600380820154908401558101549101555f8281611aef565b60248b8086634e487b7160e01b825252fd5b60248a601185634e487b7160e01b835252fd5b6024888851907f97a27f060000000000000000000000000000000000000000000000000000000082526004820152fd5b505050565b91929015611c3a5750815115611bed575090565b3b15611bf65790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b825190915015611c4d5750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401525f935b828510611c91575050604492505f838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350611c6f565b6001600160a01b03165f52607260205260405f209060018201549180546067548114611d0e5760025f9201908154915b828410611ce75750505050565b90919294611d00611d0691610459856109978a876114d8565b95611559565b929190611cda565b5050565b6068548015611d2757606a5461169692611771565b5090565b6001600160a01b03611d3c82611caa565b91165f52607260205260405f209060018201556067549055565b6067545f93928492828214611e755760018101549182811015611e6f575081905b815f1993848601958611611e5b576002830154611d939161159c565b848101818111611e475780831115611e07575b505050611db290611f51565b92865260736020526004604087200154918101908111611df35761045992916004604088611dee94611696999a5260736020522001549061158f565b6116fe565b602486634e487b7160e01b81526011600452fd5b91949293509190808611611e2957505050611696949550906104599291611e7b565b95610459611db29397611e3d939686611e7b565b9491905f80611da6565b60248a634e487b7160e01b81526011600452fd5b602489634e487b7160e01b81526011600452fd5b90611d77565b50505050565b5f9283526073602052604083206003015492939290915f198101908111611ec45760039182604087611ebb9461169698995260736020522001549061158f565b910154906116fe565b602485634e487b7160e01b81526011600452fd5b6067546001820154906002830154611ef0818461159c565b8210801590611f48575b611f405781611f0c611f11928561159c565b61158f565b918103611f2357506116969154611699565b5f1981019081116115675761169692611f3b91611f98565b611699565b505050505f90565b50828210611efa565b611f64600182015460028301549061159c565b5f1981019190821161156757816067541115611f92576004611f898261169694611f98565b9101549061158f565b50505f90565b90611fac600182015460028301549061159c565b5f198101929083116115675782816116969410611fe3575b505f526073602052600360405f20910154906001810154905491611771565b90505f611fc456fea2646970667358221220586c3927e2591c1f3aee36fc546829f6692d32bf4e54f5b0202df53e086772df64736f6c63430008140033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 27 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.