Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
VxPremia
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; import {PremiaStaking} from "./PremiaStaking.sol"; import {PremiaStakingStorage} from "./PremiaStakingStorage.sol"; import {VxPremiaStorage} from "./VxPremiaStorage.sol"; import {IVxPremia} from "./IVxPremia.sol"; import {IProxyManager} from "../core/IProxyManager.sol"; /** * @author Premia * @title A contract allowing you to use your locked Premia as voting power for mining weights */ contract VxPremia is IVxPremia, PremiaStaking { address private immutable PROXY_MANAGER; constructor( address proxyManager, address lzEndpoint, address premia, address rewardToken, address exchangeHelper ) PremiaStaking(lzEndpoint, premia, rewardToken, exchangeHelper) { PROXY_MANAGER = proxyManager; } function _beforeUnstake(address user, uint256 amount) internal override { uint256 votingPowerUnstaked = _calculateUserPower( amount, PremiaStakingStorage.layout().userInfo[user].stakePeriod ); _subtractExtraUserVotes( VxPremiaStorage.layout(), user, votingPowerUnstaked ); } /** * @notice subtract user votes, starting from the end of the list, if not enough voting power is left after amountUnstaked is unstaked */ function _subtractExtraUserVotes( VxPremiaStorage.Layout storage l, address user, uint256 amountUnstaked ) internal { uint256 votingPower = _calculateUserPower( _balanceOf(user), PremiaStakingStorage.layout().userInfo[user].stakePeriod ); uint256 votingPowerUsed = _calculateUserVotingPowerUsed(user); uint256 votingPowerLeftAfterUnstake = votingPower - amountUnstaked; unchecked { if (votingPowerUsed > votingPowerLeftAfterUnstake) { _subtractUserVotes( l, user, votingPowerUsed - votingPowerLeftAfterUnstake ); } } } /** * @notice subtract user votes, starting from the end of the list */ function _subtractUserVotes( VxPremiaStorage.Layout storage l, address user, uint256 amount ) internal { VxPremiaStorage.Vote[] storage userVotes = l.userVotes[user]; unchecked { for (uint256 i = userVotes.length; i > 0; ) { VxPremiaStorage.Vote memory vote = userVotes[--i]; uint256 votesRemoved; if (amount < vote.amount) { votesRemoved = amount; userVotes[i].amount -= amount; } else { votesRemoved = vote.amount; userVotes.pop(); } amount -= votesRemoved; l.votes[vote.version][vote.target] -= votesRemoved; emit RemoveVote(user, vote.version, vote.target, votesRemoved); if (amount == 0) break; } } } function _calculateUserVotingPowerUsed( address user ) internal view returns (uint256 votingPowerUsed) { VxPremiaStorage.Vote[] memory userVotes = VxPremiaStorage .layout() .userVotes[user]; unchecked { for (uint256 i = 0; i < userVotes.length; i++) { votingPowerUsed += userVotes[i].amount; } } } /** * @inheritdoc IVxPremia */ function getPoolVotes( VxPremiaStorage.VoteVersion version, bytes memory target ) external view returns (uint256) { return VxPremiaStorage.layout().votes[version][target]; } /** * @inheritdoc IVxPremia */ function getUserVotes( address user ) external view returns (VxPremiaStorage.Vote[] memory) { return VxPremiaStorage.layout().userVotes[user]; } /** * @inheritdoc IVxPremia */ function castVotes(VxPremiaStorage.Vote[] memory votes) external { VxPremiaStorage.Layout storage l = VxPremiaStorage.layout(); uint256 userVotingPower = _calculateUserPower( _balanceOf(msg.sender), PremiaStakingStorage.layout().userInfo[msg.sender].stakePeriod ); VxPremiaStorage.Vote[] storage userVotes = l.userVotes[msg.sender]; // Remove previous votes _resetUserVotes(l, userVotes, msg.sender); address[] memory poolList = IProxyManager(PROXY_MANAGER).getPoolList(); // Cast new votes uint256 votingPowerUsed = 0; for (uint256 i = 0; i < votes.length; i++) { VxPremiaStorage.Vote memory vote = votes[i]; votingPowerUsed += vote.amount; if (votingPowerUsed > userVotingPower) revert VxPremia__NotEnoughVotingPower(); // abi.encodePacked on [address, bool] uses 20 bytes for the address and 1 byte for the bool if ( vote.version != VxPremiaStorage.VoteVersion.V2 || vote.target.length != 21 ) revert VxPremia__InvalidVoteTarget(); // Check that the pool address is valid address contractAddress = address( uint160(uint256(bytes32(vote.target)) >> 96) // We need to shift by 96, as we want the 160 most significant bits, which are the pool address ); bool isValid = false; for (uint256 j = 0; j < poolList.length; j++) { if (contractAddress == poolList[j]) { isValid = true; break; } } if (isValid == false) revert VxPremia__InvalidPoolAddress(); userVotes.push(vote); l.votes[vote.version][vote.target] += vote.amount; emit AddVote(msg.sender, vote.version, vote.target, vote.amount); } } function _resetUserVotes( VxPremiaStorage.Layout storage l, VxPremiaStorage.Vote[] storage userVotes, address user ) internal { for (uint256 i = userVotes.length; i > 0; ) { VxPremiaStorage.Vote memory vote = userVotes[--i]; l.votes[vote.version][vote.target] -= vote.amount; emit RemoveVote(user, vote.version, vote.target, vote.amount); userVotes.pop(); } } function resetUserVotes(address user) external onlyOwner { VxPremiaStorage.Layout storage l = VxPremiaStorage.layout(); VxPremiaStorage.Vote[] storage userVotes = l.userVotes[user]; _resetUserVotes(l, userVotes, user); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC173Internal } from '../../interfaces/IERC173Internal.sol'; interface IOwnableInternal is IERC173Internal { error Ownable__NotOwner(); error Ownable__NotTransitiveOwner(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC173 } from '../../interfaces/IERC173.sol'; import { AddressUtils } from '../../utils/AddressUtils.sol'; import { IOwnableInternal } from './IOwnableInternal.sol'; import { OwnableStorage } from './OwnableStorage.sol'; abstract contract OwnableInternal is IOwnableInternal { using AddressUtils for address; modifier onlyOwner() { if (msg.sender != _owner()) revert Ownable__NotOwner(); _; } modifier onlyTransitiveOwner() { if (msg.sender != _transitiveOwner()) revert Ownable__NotTransitiveOwner(); _; } function _owner() internal view virtual returns (address) { return OwnableStorage.layout().owner; } function _transitiveOwner() internal view virtual returns (address owner) { owner = _owner(); while (owner.isContract()) { try IERC173(owner).owner() returns (address transitiveOwner) { owner = transitiveOwner; } catch { break; } } } function _transferOwnership(address account) internal virtual { _setOwner(account); } function _setOwner(address account) internal virtual { OwnableStorage.Layout storage l = OwnableStorage.layout(); emit OwnershipTransferred(l.owner, account); l.owner = account; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; library OwnableStorage { struct Layout { address owner; } bytes32 internal constant STORAGE_SLOT = keccak256('solidstate.contracts.storage.Ownable'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title Elliptic Curve Digital Signature Algorithm (ECDSA) operations * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license) */ library ECDSA { error ECDSA__InvalidS(); error ECDSA__InvalidSignature(); error ECDSA__InvalidSignatureLength(); error ECDSA__InvalidV(); /** * @notice recover signer of hashed message from signature * @param hash hashed data payload * @param signature signed data payload * @return recovered message signer */ function recover( bytes32 hash, bytes memory signature ) internal pure returns (address) { if (signature.length != 65) revert ECDSA__InvalidSignatureLength(); bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return recover(hash, v, r, s); } /** * @notice recover signer of hashed message from signature v, r, and s values * @param hash hashed data payload * @param v signature "v" value * @param r signature "r" value * @param s signature "s" value * @return recovered message signer */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if ( uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 ) revert ECDSA__InvalidS(); if (v != 27 && v != 28) revert ECDSA__InvalidV(); // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) revert ECDSA__InvalidSignature(); return signer; } /** * @notice generate an "Ethereum Signed Message" in the format returned by the eth_sign JSON-RPC method * @param hash hashed data payload * @return signed message hash */ function toEthSignedMessageHash( bytes32 hash ) internal pure returns (bytes32) { return keccak256( abi.encodePacked('\x19Ethereum Signed Message:\n32', hash) ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC165Internal } from './IERC165Internal.sol'; /** * @title ERC165 interface registration interface * @dev see https://eips.ethereum.org/EIPS/eip-165 */ interface IERC165 is IERC165Internal { /** * @notice query whether contract has registered support for given interface * @param interfaceId interface id * @return bool whether interface is supported */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC165Internal } from './IERC165Internal.sol'; /** * @title ERC165 interface registration interface */ interface IERC165Internal { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC173Internal } from './IERC173Internal.sol'; /** * @title Contract ownership standard interface * @dev see https://eips.ethereum.org/EIPS/eip-173 */ interface IERC173 is IERC173Internal { /** * @notice get the ERC173 contract owner * @return conrtact owner */ function owner() external view returns (address); /** * @notice transfer contract ownership to new account * @param account address of new owner */ function transferOwnership(address account) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title Partial ERC173 interface needed by internal functions */ interface IERC173Internal { event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Internal } from './IERC20Internal.sol'; /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ interface IERC20 is IERC20Internal { /** * @notice query the total minted token supply * @return token supply */ function totalSupply() external view returns (uint256); /** * @notice query the token balance of given account * @param account address to query * @return token balance */ function balanceOf(address account) external view returns (uint256); /** * @notice query the allowance granted from given holder to given spender * @param holder approver of allowance * @param spender recipient of allowance * @return token allowance */ function allowance( address holder, address spender ) external view returns (uint256); /** * @notice grant approval to spender to spend tokens * @dev prefer ERC20Extended functions to avoid transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729) * @param spender recipient of allowance * @param amount quantity of tokens approved for spending * @return success status (always true; otherwise function should revert) */ function approve(address spender, uint256 amount) external returns (bool); /** * @notice transfer tokens to given recipient * @param recipient beneficiary of token transfer * @param amount quantity of tokens to transfer * @return success status (always true; otherwise function should revert) */ function transfer( address recipient, uint256 amount ) external returns (bool); /** * @notice transfer tokens to given recipient on behalf of given holder * @param holder holder of tokens prior to transfer * @param recipient beneficiary of token transfer * @param amount quantity of tokens to transfer * @return success status (always true; otherwise function should revert) */ function transferFrom( address holder, address recipient, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title Partial ERC20 interface needed by internal functions */ interface IERC20Internal { event Transfer(address indexed from, address indexed to, uint256 value); event Approval( address indexed owner, address indexed spender, uint256 value ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC165 } from '../../../interfaces/IERC165.sol'; import { IERC165Base } from './IERC165Base.sol'; import { ERC165BaseInternal } from './ERC165BaseInternal.sol'; import { ERC165BaseStorage } from './ERC165BaseStorage.sol'; /** * @title ERC165 implementation */ abstract contract ERC165Base is IERC165Base, ERC165BaseInternal { /** * @inheritdoc IERC165 */ function supportsInterface(bytes4 interfaceId) public view returns (bool) { return _supportsInterface(interfaceId); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC165BaseInternal } from './IERC165BaseInternal.sol'; import { ERC165BaseStorage } from './ERC165BaseStorage.sol'; /** * @title ERC165 implementation */ abstract contract ERC165BaseInternal is IERC165BaseInternal { /** * @notice indicates whether an interface is already supported based on the interfaceId * @param interfaceId id of interface to check * @return bool indicating whether interface is supported */ function _supportsInterface( bytes4 interfaceId ) internal view returns (bool) { return ERC165BaseStorage.layout().supportedInterfaces[interfaceId]; } /** * @notice sets status of interface support * @param interfaceId id of interface to set status for * @param status boolean indicating whether interface will be set as supported */ function _setSupportsInterface(bytes4 interfaceId, bool status) internal { if (interfaceId == 0xffffffff) revert ERC165Base__InvalidInterfaceId(); ERC165BaseStorage.layout().supportedInterfaces[interfaceId] = status; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; library ERC165BaseStorage { struct Layout { mapping(bytes4 => bool) supportedInterfaces; } bytes32 internal constant STORAGE_SLOT = keccak256('solidstate.contracts.storage.ERC165Base'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC165 } from '../../../interfaces/IERC165.sol'; import { IERC165BaseInternal } from './IERC165BaseInternal.sol'; interface IERC165Base is IERC165, IERC165BaseInternal {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC165Internal } from '../../../interfaces/IERC165Internal.sol'; interface IERC165BaseInternal is IERC165Internal { error ERC165Base__InvalidInterfaceId(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20 } from '../../../interfaces/IERC20.sol'; import { IERC20Base } from './IERC20Base.sol'; import { ERC20BaseInternal } from './ERC20BaseInternal.sol'; import { ERC20BaseStorage } from './ERC20BaseStorage.sol'; /** * @title Base ERC20 implementation, excluding optional extensions */ abstract contract ERC20Base is IERC20Base, ERC20BaseInternal { /** * @inheritdoc IERC20 */ function totalSupply() external view returns (uint256) { return _totalSupply(); } /** * @inheritdoc IERC20 */ function balanceOf(address account) external view returns (uint256) { return _balanceOf(account); } /** * @inheritdoc IERC20 */ function allowance( address holder, address spender ) external view returns (uint256) { return _allowance(holder, spender); } /** * @inheritdoc IERC20 */ function approve(address spender, uint256 amount) external returns (bool) { return _approve(msg.sender, spender, amount); } /** * @inheritdoc IERC20 */ function transfer( address recipient, uint256 amount ) external returns (bool) { return _transfer(msg.sender, recipient, amount); } /** * @inheritdoc IERC20 */ function transferFrom( address holder, address recipient, uint256 amount ) external returns (bool) { return _transferFrom(holder, recipient, amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20BaseInternal } from './IERC20BaseInternal.sol'; import { ERC20BaseStorage } from './ERC20BaseStorage.sol'; /** * @title Base ERC20 internal functions, excluding optional extensions */ abstract contract ERC20BaseInternal is IERC20BaseInternal { /** * @notice query the total minted token supply * @return token supply */ function _totalSupply() internal view virtual returns (uint256) { return ERC20BaseStorage.layout().totalSupply; } /** * @notice query the token balance of given account * @param account address to query * @return token balance */ function _balanceOf( address account ) internal view virtual returns (uint256) { return ERC20BaseStorage.layout().balances[account]; } /** * @notice query the allowance granted from given holder to given spender * @param holder approver of allowance * @param spender recipient of allowance * @return token allowance */ function _allowance( address holder, address spender ) internal view virtual returns (uint256) { return ERC20BaseStorage.layout().allowances[holder][spender]; } /** * @notice enable spender to spend tokens on behalf of holder * @param holder address on whose behalf tokens may be spent * @param spender recipient of allowance * @param amount quantity of tokens approved for spending * @return success status (always true; otherwise function should revert) */ function _approve( address holder, address spender, uint256 amount ) internal virtual returns (bool) { if (holder == address(0)) revert ERC20Base__ApproveFromZeroAddress(); if (spender == address(0)) revert ERC20Base__ApproveToZeroAddress(); ERC20BaseStorage.layout().allowances[holder][spender] = amount; emit Approval(holder, spender, amount); return true; } /** * @notice decrease spend amount granted by holder to spender * @param holder address on whose behalf tokens may be spent * @param spender address whose allowance to decrease * @param amount quantity by which to decrease allowance */ function _decreaseAllowance( address holder, address spender, uint256 amount ) internal { uint256 allowance = _allowance(holder, spender); if (amount > allowance) revert ERC20Base__InsufficientAllowance(); unchecked { _approve(holder, spender, allowance - amount); } } /** * @notice mint tokens for given account * @param account recipient of minted tokens * @param amount quantity of tokens minted */ function _mint(address account, uint256 amount) internal virtual { if (account == address(0)) revert ERC20Base__MintToZeroAddress(); _beforeTokenTransfer(address(0), account, amount); ERC20BaseStorage.Layout storage l = ERC20BaseStorage.layout(); l.totalSupply += amount; l.balances[account] += amount; emit Transfer(address(0), account, amount); } /** * @notice burn tokens held by given account * @param account holder of burned tokens * @param amount quantity of tokens burned */ function _burn(address account, uint256 amount) internal virtual { if (account == address(0)) revert ERC20Base__BurnFromZeroAddress(); _beforeTokenTransfer(account, address(0), amount); ERC20BaseStorage.Layout storage l = ERC20BaseStorage.layout(); uint256 balance = l.balances[account]; if (amount > balance) revert ERC20Base__BurnExceedsBalance(); unchecked { l.balances[account] = balance - amount; } l.totalSupply -= amount; emit Transfer(account, address(0), amount); } /** * @notice transfer tokens from holder to recipient * @param holder owner of tokens to be transferred * @param recipient beneficiary of transfer * @param amount quantity of tokens transferred * @return success status (always true; otherwise function should revert) */ function _transfer( address holder, address recipient, uint256 amount ) internal virtual returns (bool) { if (holder == address(0)) revert ERC20Base__TransferFromZeroAddress(); if (recipient == address(0)) revert ERC20Base__TransferToZeroAddress(); _beforeTokenTransfer(holder, recipient, amount); ERC20BaseStorage.Layout storage l = ERC20BaseStorage.layout(); uint256 holderBalance = l.balances[holder]; if (amount > holderBalance) revert ERC20Base__TransferExceedsBalance(); unchecked { l.balances[holder] = holderBalance - amount; } l.balances[recipient] += amount; emit Transfer(holder, recipient, amount); return true; } /** * @notice transfer tokens to given recipient on behalf of given holder * @param holder holder of tokens prior to transfer * @param recipient beneficiary of token transfer * @param amount quantity of tokens to transfer * @return success status (always true; otherwise function should revert) */ function _transferFrom( address holder, address recipient, uint256 amount ) internal virtual returns (bool) { _decreaseAllowance(holder, msg.sender, amount); _transfer(holder, recipient, amount); return true; } /** * @notice ERC20 hook, called before all transfers including mint and burn * @dev function should be overridden and new implementation must call super * @param from sender of tokens * @param to receiver of tokens * @param amount quantity of tokens transferred */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; library ERC20BaseStorage { struct Layout { mapping(address => uint256) balances; mapping(address => mapping(address => uint256)) allowances; uint256 totalSupply; } bytes32 internal constant STORAGE_SLOT = keccak256('solidstate.contracts.storage.ERC20Base'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20 } from '../../../interfaces/IERC20.sol'; import { IERC20BaseInternal } from './IERC20BaseInternal.sol'; /** * @title ERC20 base interface */ interface IERC20Base is IERC20BaseInternal, IERC20 { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Internal } from '../../../interfaces/IERC20Internal.sol'; /** * @title ERC20 base interface */ interface IERC20BaseInternal is IERC20Internal { error ERC20Base__ApproveFromZeroAddress(); error ERC20Base__ApproveToZeroAddress(); error ERC20Base__BurnExceedsBalance(); error ERC20Base__BurnFromZeroAddress(); error ERC20Base__InsufficientAllowance(); error ERC20Base__MintToZeroAddress(); error ERC20Base__TransferExceedsBalance(); error ERC20Base__TransferFromZeroAddress(); error ERC20Base__TransferToZeroAddress(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Extended } from './IERC20Extended.sol'; import { ERC20ExtendedInternal } from './ERC20ExtendedInternal.sol'; /** * @title ERC20 safe approval extensions * @dev mitigations for transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729) */ abstract contract ERC20Extended is IERC20Extended, ERC20ExtendedInternal { /** * @inheritdoc IERC20Extended */ function increaseAllowance( address spender, uint256 amount ) external returns (bool) { return _increaseAllowance(spender, amount); } /** * @inheritdoc IERC20Extended */ function decreaseAllowance( address spender, uint256 amount ) external returns (bool) { return _decreaseAllowance(spender, amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { ERC20BaseInternal, ERC20BaseStorage } from '../base/ERC20Base.sol'; import { IERC20ExtendedInternal } from './IERC20ExtendedInternal.sol'; /** * @title ERC20 safe approval extensions * @dev mitigations for transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729) */ abstract contract ERC20ExtendedInternal is ERC20BaseInternal, IERC20ExtendedInternal { /** * @notice increase spend amount granted to spender * @param spender address whose allowance to increase * @param amount quantity by which to increase allowance * @return success status (always true; otherwise function will revert) */ function _increaseAllowance( address spender, uint256 amount ) internal virtual returns (bool) { uint256 allowance = _allowance(msg.sender, spender); unchecked { if (allowance > allowance + amount) revert ERC20Extended__ExcessiveAllowance(); return _approve(msg.sender, spender, allowance + amount); } } /** * @notice decrease spend amount granted to spender * @param spender address whose allowance to decrease * @param amount quantity by which to decrease allowance * @return success status (always true; otherwise function will revert) */ function _decreaseAllowance( address spender, uint256 amount ) internal virtual returns (bool) { _decreaseAllowance(msg.sender, spender, amount); return true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20ExtendedInternal } from './IERC20ExtendedInternal.sol'; /** * @title ERC20 extended interface */ interface IERC20Extended is IERC20ExtendedInternal { /** * @notice increase spend amount granted to spender * @param spender address whose allowance to increase * @param amount quantity by which to increase allowance * @return success status (always true; otherwise function will revert) */ function increaseAllowance( address spender, uint256 amount ) external returns (bool); /** * @notice decrease spend amount granted to spender * @param spender address whose allowance to decrease * @param amount quantity by which to decrease allowance * @return success status (always true; otherwise function will revert) */ function decreaseAllowance( address spender, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20BaseInternal } from '../base/IERC20BaseInternal.sol'; /** * @title ERC20 extended internal interface */ interface IERC20ExtendedInternal is IERC20BaseInternal { error ERC20Extended__ExcessiveAllowance(); error ERC20Extended__InsufficientAllowance(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Base } from './base/IERC20Base.sol'; import { IERC20Extended } from './extended/IERC20Extended.sol'; import { IERC20Metadata } from './metadata/IERC20Metadata.sol'; import { IERC20Permit } from './permit/IERC20Permit.sol'; interface ISolidStateERC20 is IERC20Base, IERC20Extended, IERC20Metadata, IERC20Permit {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Metadata } from './IERC20Metadata.sol'; import { ERC20MetadataInternal } from './ERC20MetadataInternal.sol'; /** * @title ERC20 metadata extensions */ abstract contract ERC20Metadata is IERC20Metadata, ERC20MetadataInternal { /** * @inheritdoc IERC20Metadata */ function name() external view returns (string memory) { return _name(); } /** * @inheritdoc IERC20Metadata */ function symbol() external view returns (string memory) { return _symbol(); } /** * @inheritdoc IERC20Metadata */ function decimals() external view returns (uint8) { return _decimals(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20MetadataInternal } from './IERC20MetadataInternal.sol'; import { ERC20MetadataStorage } from './ERC20MetadataStorage.sol'; /** * @title ERC20Metadata internal functions */ abstract contract ERC20MetadataInternal is IERC20MetadataInternal { /** * @notice return token name * @return token name */ function _name() internal view virtual returns (string memory) { return ERC20MetadataStorage.layout().name; } /** * @notice return token symbol * @return token symbol */ function _symbol() internal view virtual returns (string memory) { return ERC20MetadataStorage.layout().symbol; } /** * @notice return token decimals, generally used only for display purposes * @return token decimals */ function _decimals() internal view virtual returns (uint8) { return ERC20MetadataStorage.layout().decimals; } function _setName(string memory name) internal virtual { ERC20MetadataStorage.layout().name = name; } function _setSymbol(string memory symbol) internal virtual { ERC20MetadataStorage.layout().symbol = symbol; } function _setDecimals(uint8 decimals) internal virtual { ERC20MetadataStorage.layout().decimals = decimals; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; library ERC20MetadataStorage { struct Layout { string name; string symbol; uint8 decimals; } bytes32 internal constant STORAGE_SLOT = keccak256('solidstate.contracts.storage.ERC20Metadata'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20MetadataInternal } from './IERC20MetadataInternal.sol'; /** * @title ERC20 metadata interface */ interface IERC20Metadata is IERC20MetadataInternal { /** * @notice return token name * @return token name */ function name() external view returns (string memory); /** * @notice return token symbol * @return token symbol */ function symbol() external view returns (string memory); /** * @notice return token decimals, generally used only for display purposes * @return token decimals */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title ERC20 metadata internal interface */ interface IERC20MetadataInternal { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { ERC20Base } from '../base/ERC20Base.sol'; import { ERC20Metadata } from '../metadata/ERC20Metadata.sol'; import { ERC20PermitInternal } from './ERC20PermitInternal.sol'; import { ERC20PermitStorage } from './ERC20PermitStorage.sol'; import { IERC2612 } from './IERC2612.sol'; import { IERC20Permit } from './IERC20Permit.sol'; /** * @title ERC20 extension with support for ERC2612 permits * @dev derived from https://github.com/soliditylabs/ERC20-Permit (MIT license) */ abstract contract ERC20Permit is IERC20Permit, ERC20PermitInternal { /** * @inheritdoc IERC2612 */ function DOMAIN_SEPARATOR() external view returns (bytes32 domainSeparator) { return _DOMAIN_SEPARATOR(); } /** * @inheritdoc IERC2612 */ function nonces(address owner) public view returns (uint256) { return _nonces(owner); } /** * @inheritdoc IERC2612 */ function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { _permit(owner, spender, amount, deadline, v, r, s); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { ECDSA } from '../../../cryptography/ECDSA.sol'; import { ERC20BaseInternal } from '../base/ERC20BaseInternal.sol'; import { ERC20MetadataInternal } from '../metadata/ERC20MetadataInternal.sol'; import { ERC20PermitStorage } from './ERC20PermitStorage.sol'; import { IERC20PermitInternal } from './IERC20PermitInternal.sol'; /** * @title ERC20 extension with support for ERC2612 permits * @dev derived from https://github.com/soliditylabs/ERC20-Permit (MIT license) */ abstract contract ERC20PermitInternal is ERC20BaseInternal, ERC20MetadataInternal, IERC20PermitInternal { using ECDSA for bytes32; /** * @notice return the EIP-712 domain separator unique to contract and chain * @return domainSeparator domain separator */ function _DOMAIN_SEPARATOR() internal view returns (bytes32 domainSeparator) { domainSeparator = ERC20PermitStorage.layout().domainSeparators[ _chainId() ]; if (domainSeparator == 0x00) { domainSeparator = _calculateDomainSeparator(); } } /** * @notice get the current ERC2612 nonce for the given address * @return current nonce */ function _nonces(address owner) internal view returns (uint256) { return ERC20PermitStorage.layout().nonces[owner]; } /** * @notice calculate unique EIP-712 domain separator * @return domainSeparator domain separator */ function _calculateDomainSeparator() internal view returns (bytes32 domainSeparator) { // no need for assembly, running very rarely domainSeparator = keccak256( abi.encode( keccak256( 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)' ), keccak256(bytes(_name())), // ERC-20 Name keccak256(bytes('1')), // Version _chainId(), address(this) ) ); } /** * @notice get the current chain ID * @return chainId chain ID */ function _chainId() private view returns (uint256 chainId) { assembly { chainId := chainid() } } /** * @notice approve spender to transfer tokens held by owner via signature * @dev this function may be vulnerable to approval replay attacks * @param owner holder of tokens and signer of permit * @param spender beneficiary of approval * @param amount quantity of tokens to approve * @param v secp256k1 'v' value * @param r secp256k1 'r' value * @param s secp256k1 's' value * @dev If https://eips.ethereum.org/EIPS/eip-1344[ChainID] ever changes, the * EIP712 Domain Separator is automatically recalculated. */ function _permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal virtual { if (block.timestamp > deadline) revert ERC20Permit__ExpiredDeadline(); // Assembly for more efficiently computing: // bytes32 hashStruct = keccak256( // abi.encode( // _PERMIT_TYPEHASH, // owner, // spender, // amount, // _nonces[owner].current(), // deadline // ) // ); ERC20PermitStorage.Layout storage l = ERC20PermitStorage.layout(); bytes32 hashStruct; uint256 nonce = l.nonces[owner]; assembly { // Load free memory pointer let pointer := mload(64) // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") mstore( pointer, 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9 ) mstore(add(pointer, 32), owner) mstore(add(pointer, 64), spender) mstore(add(pointer, 96), amount) mstore(add(pointer, 128), nonce) mstore(add(pointer, 160), deadline) hashStruct := keccak256(pointer, 192) } bytes32 domainSeparator = l.domainSeparators[_chainId()]; if (domainSeparator == 0x00) { domainSeparator = _calculateDomainSeparator(); l.domainSeparators[_chainId()] = domainSeparator; } // Assembly for more efficient computing: // bytes32 hash = keccak256( // abi.encodePacked(uint16(0x1901), domainSeparator, hashStruct) // ); bytes32 hash; assembly { // Load free memory pointer let pointer := mload(64) mstore( pointer, 0x1901000000000000000000000000000000000000000000000000000000000000 ) // EIP191 header mstore(add(pointer, 2), domainSeparator) // EIP712 domain hash mstore(add(pointer, 34), hashStruct) // Hash of struct hash := keccak256(pointer, 66) } address signer = hash.recover(v, r, s); if (signer != owner) revert ERC20Permit__InvalidSignature(); l.nonces[owner]++; _approve(owner, spender, amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; library ERC20PermitStorage { struct Layout { mapping(address => uint256) nonces; // Mapping of ChainID to domain separators. This is a very gas efficient way // to not recalculate the domain separator on every call, while still // automatically detecting ChainID changes. mapping(uint256 => bytes32) domainSeparators; } bytes32 internal constant STORAGE_SLOT = keccak256('solidstate.contracts.storage.ERC20Permit'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20Metadata } from '../metadata/IERC20Metadata.sol'; import { IERC2612 } from './IERC2612.sol'; import { IERC20PermitInternal } from './IERC20PermitInternal.sol'; // TODO: note that IERC20Metadata is needed for eth-permit library interface IERC20Permit is IERC20PermitInternal, IERC2612 { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC2612Internal } from './IERC2612Internal.sol'; interface IERC20PermitInternal is IERC2612Internal { error ERC20Permit__ExpiredDeadline(); error ERC20Permit__InvalidSignature(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC2612Internal } from './IERC2612Internal.sol'; /** * @title ERC2612 interface * @dev see https://eips.ethereum.org/EIPS/eip-2612. */ interface IERC2612 is IERC2612Internal { /** * @notice return the EIP-712 domain separator unique to contract and chain * @return domainSeparator domain separator */ function DOMAIN_SEPARATOR() external view returns (bytes32 domainSeparator); /** * @notice get the current ERC2612 nonce for the given address * @return current nonce */ function nonces(address owner) external view returns (uint256); /** * @notice approve spender to transfer tokens held by owner via signature * @dev this function may be vulnerable to approval replay attacks * @param owner holder of tokens and signer of permit * @param spender beneficiary of approval * @param amount quantity of tokens to approve * @param v secp256k1 'v' value * @param r secp256k1 'r' value * @param s secp256k1 's' value */ function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; interface IERC2612Internal {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { ISolidStateERC20 } from './ISolidStateERC20.sol'; import { ERC20Base } from './base/ERC20Base.sol'; import { ERC20Extended } from './extended/ERC20Extended.sol'; import { ERC20Metadata } from './metadata/ERC20Metadata.sol'; import { ERC20Permit } from './permit/ERC20Permit.sol'; /** * @title SolidState ERC20 implementation, including recommended extensions */ abstract contract SolidStateERC20 is ISolidStateERC20, ERC20Base, ERC20Extended, ERC20Metadata, ERC20Permit { }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { UintUtils } from './UintUtils.sol'; library AddressUtils { using UintUtils for uint256; error AddressUtils__InsufficientBalance(); error AddressUtils__NotContract(); error AddressUtils__SendValueFailed(); function toString(address account) internal pure returns (string memory) { return uint256(uint160(account)).toHexString(20); } function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; } function sendValue(address payable account, uint256 amount) internal { (bool success, ) = account.call{ value: amount }(''); if (!success) revert AddressUtils__SendValueFailed(); } function functionCall( address target, bytes memory data ) internal returns (bytes memory) { return functionCall(target, data, 'AddressUtils: failed low-level call'); } function functionCall( address target, bytes memory data, string memory error ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, error); } function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue( target, data, value, 'AddressUtils: failed low-level call with value' ); } function functionCallWithValue( address target, bytes memory data, uint256 value, string memory error ) internal returns (bytes memory) { if (value > address(this).balance) revert AddressUtils__InsufficientBalance(); return _functionCallWithValue(target, data, value, error); } /** * @notice execute arbitrary external call with limited gas usage and amount of copied return data * @dev derived from https://github.com/nomad-xyz/ExcessivelySafeCall (MIT License) * @param target recipient of call * @param gasAmount gas allowance for call * @param value native token value to include in call * @param maxCopy maximum number of bytes to copy from return data * @param data encoded call data * @return success whether call is successful * @return returnData copied return data */ function excessivelySafeCall( address target, uint256 gasAmount, uint256 value, uint16 maxCopy, bytes memory data ) internal returns (bool success, bytes memory returnData) { returnData = new bytes(maxCopy); assembly { // execute external call via assembly to avoid automatic copying of return data success := call( gasAmount, target, value, add(data, 0x20), mload(data), 0, 0 ) // determine whether to limit amount of data to copy let toCopy := returndatasize() if gt(toCopy, maxCopy) { toCopy := maxCopy } // store the length of the copied bytes mstore(returnData, toCopy) // copy the bytes from returndata[0:toCopy] returndatacopy(add(returnData, 0x20), 0, toCopy) } } function _functionCallWithValue( address target, bytes memory data, uint256 value, string memory error ) private returns (bytes memory) { if (!isContract(target)) revert AddressUtils__NotContract(); (bool success, bytes memory returnData) = target.call{ value: value }( data ); if (success) { return returnData; } else if (returnData.length > 0) { assembly { let returnData_size := mload(returnData) revert(add(32, returnData), returnData_size) } } else { revert(error); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; library Math { /** * @notice calculate the absolute value of a number * @param a number whose absoluve value to calculate * @return absolute value */ function abs(int256 a) internal pure returns (uint256) { return uint256(a < 0 ? -a : a); } /** * @notice select the greater of two numbers * @param a first number * @param b second number * @return greater number */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @notice select the lesser of two numbers * @param a first number * @param b second number * @return lesser number */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? b : a; } /** * @notice calculate the average of two numbers, rounded down * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license) * @param a first number * @param b second number * @return mean value */ function average(uint256 a, uint256 b) internal pure returns (uint256) { unchecked { return (a & b) + ((a ^ b) >> 1); } } /** * @notice estimate square root of number * @dev uses Babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) * @param x input number * @return y square root */ function sqrt(uint256 x) internal pure returns (uint256 y) { uint256 z = (x + 1) >> 1; y = x; while (z < y) { y = z; z = (x / z + z) >> 1; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; import { IERC20 } from '../interfaces/IERC20.sol'; import { AddressUtils } from './AddressUtils.sol'; /** * @title Safe ERC20 interaction library * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license) */ library SafeERC20 { using AddressUtils for address; error SafeERC20__ApproveFromNonZeroToNonZero(); error SafeERC20__DecreaseAllowanceBelowZero(); error SafeERC20__OperationFailed(); function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } /** * @dev safeApprove (like approve) should only be called when setting an initial allowance or when resetting it to zero; otherwise prefer safeIncreaseAllowance and safeDecreaseAllowance */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { if ((value != 0) && (token.allowance(address(this), spender) != 0)) revert SafeERC20__ApproveFromNonZeroToNonZero(); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, value) ); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); if (oldAllowance < value) revert SafeERC20__DecreaseAllowanceBelowZero(); uint256 newAllowance = oldAllowance - value; _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } } /** * @notice send transaction data and check validity of return value, if present * @param token ERC20 token interface * @param data transaction data */ function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall( data, 'SafeERC20: low-level call failed' ); if (returndata.length > 0) { if (!abi.decode(returndata, (bool))) revert SafeERC20__OperationFailed(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /** * @title utility functions for uint256 operations * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license) */ library UintUtils { error UintUtils__InsufficientHexLength(); bytes16 private constant HEX_SYMBOLS = '0123456789abcdef'; function add(uint256 a, int256 b) internal pure returns (uint256) { return b < 0 ? sub(a, -b) : a + uint256(b); } function sub(uint256 a, int256 b) internal pure returns (uint256) { return b < 0 ? add(a, -b) : a - uint256(b); } function toString(uint256 value) internal pure returns (string memory) { if (value == 0) { return '0'; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return '0x00'; } uint256 length = 0; for (uint256 temp = value; temp != 0; temp >>= 8) { unchecked { length++; } } return toHexString(value, length); } function toHexString( uint256 value, uint256 length ) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = '0'; buffer[1] = 'x'; unchecked { for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = HEX_SYMBOLS[value & 0xf]; value >>= 4; } } if (value != 0) revert UintUtils__InsufficientHexLength(); return string(buffer); } }
// SPDX-License-Identifier: BSD-4-Clause /* * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. * Author: Mikhail Vladimirov <[email protected]> */ pragma solidity ^0.8.0; /** * Smart contract library of mathematical functions operating with signed * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is * basically a simple fraction whose numerator is signed 128-bit integer and * denominator is 2^64. As long as denominator is always the same, there is no * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are * represented by int128 type holding only the numerator. */ library ABDKMath64x64 { /* * Minimum value signed 64.64-bit fixed point number may have. */ int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; /* * Maximum value signed 64.64-bit fixed point number may have. */ int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; /** * Convert signed 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromInt (int256 x) internal pure returns (int128) { unchecked { require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); return int128 (x << 64); } } /** * Convert signed 64.64 fixed point number into signed 64-bit integer number * rounding down. * * @param x signed 64.64-bit fixed point number * @return signed 64-bit integer number */ function toInt (int128 x) internal pure returns (int64) { unchecked { return int64 (x >> 64); } } /** * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromUInt (uint256 x) internal pure returns (int128) { unchecked { require (x <= 0x7FFFFFFFFFFFFFFF); return int128 (int256 (x << 64)); } } /** * Convert signed 64.64 fixed point number into unsigned 64-bit integer * number rounding down. Revert on underflow. * * @param x signed 64.64-bit fixed point number * @return unsigned 64-bit integer number */ function toUInt (int128 x) internal pure returns (uint64) { unchecked { require (x >= 0); return uint64 (uint128 (x >> 64)); } } /** * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point * number rounding down. Revert on overflow. * * @param x signed 128.128-bin fixed point number * @return signed 64.64-bit fixed point number */ function from128x128 (int256 x) internal pure returns (int128) { unchecked { int256 result = x >> 64; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Convert signed 64.64 fixed point number into signed 128.128 fixed point * number. * * @param x signed 64.64-bit fixed point number * @return signed 128.128 fixed point number */ function to128x128 (int128 x) internal pure returns (int256) { unchecked { return int256 (x) << 64; } } /** * Calculate x + y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function add (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) + y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x - y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sub (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) - y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x * y rounding down. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function mul (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) * y >> 64; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point * number and y is signed 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y signed 256-bit integer number * @return signed 256-bit integer number */ function muli (int128 x, int256 y) internal pure returns (int256) { unchecked { if (x == MIN_64x64) { require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && y <= 0x1000000000000000000000000000000000000000000000000); return -y << 63; } else { bool negativeResult = false; if (x < 0) { x = -x; negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint256 absoluteResult = mulu (x, uint256 (y)); if (negativeResult) { require (absoluteResult <= 0x8000000000000000000000000000000000000000000000000000000000000000); return -int256 (absoluteResult); // We rely on overflow behavior here } else { require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int256 (absoluteResult); } } } } /** * Calculate x * y rounding down, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y unsigned 256-bit integer number * @return unsigned 256-bit integer number */ function mulu (int128 x, uint256 y) internal pure returns (uint256) { unchecked { if (y == 0) return 0; require (x >= 0); uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; uint256 hi = uint256 (int256 (x)) * (y >> 128); require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); hi <<= 64; require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo); return hi + lo; } } /** * Calculate x / y rounding towards zero. Revert on overflow or when y is * zero. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function div (int128 x, int128 y) internal pure returns (int128) { unchecked { require (y != 0); int256 result = (int256 (x) << 64) / y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x / y rounding towards zero, where x and y are signed 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x signed 256-bit integer number * @param y signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function divi (int256 x, int256 y) internal pure returns (int128) { unchecked { require (y != 0); bool negativeResult = false; if (x < 0) { x = -x; // We rely on overflow behavior here negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint128 absoluteResult = divuu (uint256 (x), uint256 (y)); if (negativeResult) { require (absoluteResult <= 0x80000000000000000000000000000000); return -int128 (absoluteResult); // We rely on overflow behavior here } else { require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int128 (absoluteResult); // We rely on overflow behavior here } } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function divu (uint256 x, uint256 y) internal pure returns (int128) { unchecked { require (y != 0); uint128 result = divuu (x, y); require (result <= uint128 (MAX_64x64)); return int128 (result); } } /** * Calculate -x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function neg (int128 x) internal pure returns (int128) { unchecked { require (x != MIN_64x64); return -x; } } /** * Calculate |x|. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function abs (int128 x) internal pure returns (int128) { unchecked { require (x != MIN_64x64); return x < 0 ? -x : x; } } /** * Calculate 1 / x rounding towards zero. Revert on overflow or when x is * zero. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function inv (int128 x) internal pure returns (int128) { unchecked { require (x != 0); int256 result = int256 (0x100000000000000000000000000000000) / x; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function avg (int128 x, int128 y) internal pure returns (int128) { unchecked { return int128 ((int256 (x) + int256 (y)) >> 1); } } /** * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. * Revert on overflow or in case x * y is negative. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function gavg (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 m = int256 (x) * int256 (y); require (m >= 0); require (m < 0x4000000000000000000000000000000000000000000000000000000000000000); return int128 (sqrtu (uint256 (m))); } } /** * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y uint256 value * @return signed 64.64-bit fixed point number */ function pow (int128 x, uint256 y) internal pure returns (int128) { unchecked { bool negative = x < 0 && y & 1 == 1; uint256 absX = uint128 (x < 0 ? -x : x); uint256 absResult; absResult = 0x100000000000000000000000000000000; if (absX <= 0x10000000000000000) { absX <<= 63; while (y != 0) { if (y & 0x1 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; if (y & 0x2 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; if (y & 0x4 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; if (y & 0x8 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; y >>= 4; } absResult >>= 64; } else { uint256 absXShift = 63; if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; } if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; } if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; } if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; } if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; } if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; } uint256 resultShift = 0; while (y != 0) { require (absXShift < 64); if (y & 0x1 != 0) { absResult = absResult * absX >> 127; resultShift += absXShift; if (absResult > 0x100000000000000000000000000000000) { absResult >>= 1; resultShift += 1; } } absX = absX * absX >> 127; absXShift <<= 1; if (absX >= 0x100000000000000000000000000000000) { absX >>= 1; absXShift += 1; } y >>= 1; } require (resultShift < 64); absResult >>= 64 - resultShift; } int256 result = negative ? -int256 (absResult) : int256 (absResult); require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate sqrt (x) rounding down. Revert if x < 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sqrt (int128 x) internal pure returns (int128) { unchecked { require (x >= 0); return int128 (sqrtu (uint256 (int256 (x)) << 64)); } } /** * Calculate binary logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function log_2 (int128 x) internal pure returns (int128) { unchecked { require (x > 0); int256 msb = 0; int256 xc = x; if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore int256 result = msb - 64 << 64; uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb); for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { ux *= ux; uint256 b = ux >> 255; ux >>= 127 + b; result += bit * int256 (b); } return int128 (result); } } /** * Calculate natural logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function ln (int128 x) internal pure returns (int128) { unchecked { require (x > 0); return int128 (int256 ( uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128)); } } /** * Calculate binary exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp_2 (int128 x) internal pure returns (int128) { unchecked { require (x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow uint256 result = 0x80000000000000000000000000000000; if (x & 0x8000000000000000 > 0) result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128; if (x & 0x4000000000000000 > 0) result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128; if (x & 0x2000000000000000 > 0) result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128; if (x & 0x1000000000000000 > 0) result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128; if (x & 0x800000000000000 > 0) result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128; if (x & 0x400000000000000 > 0) result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128; if (x & 0x200000000000000 > 0) result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128; if (x & 0x100000000000000 > 0) result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128; if (x & 0x80000000000000 > 0) result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128; if (x & 0x40000000000000 > 0) result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128; if (x & 0x20000000000000 > 0) result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128; if (x & 0x10000000000000 > 0) result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128; if (x & 0x8000000000000 > 0) result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128; if (x & 0x4000000000000 > 0) result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128; if (x & 0x2000000000000 > 0) result = result * 0x1000162E525EE054754457D5995292026 >> 128; if (x & 0x1000000000000 > 0) result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128; if (x & 0x800000000000 > 0) result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128; if (x & 0x400000000000 > 0) result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128; if (x & 0x200000000000 > 0) result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128; if (x & 0x100000000000 > 0) result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128; if (x & 0x80000000000 > 0) result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128; if (x & 0x40000000000 > 0) result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128; if (x & 0x20000000000 > 0) result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128; if (x & 0x10000000000 > 0) result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128; if (x & 0x8000000000 > 0) result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128; if (x & 0x4000000000 > 0) result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128; if (x & 0x2000000000 > 0) result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128; if (x & 0x1000000000 > 0) result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128; if (x & 0x800000000 > 0) result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128; if (x & 0x400000000 > 0) result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128; if (x & 0x200000000 > 0) result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128; if (x & 0x100000000 > 0) result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128; if (x & 0x80000000 > 0) result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128; if (x & 0x40000000 > 0) result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128; if (x & 0x20000000 > 0) result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128; if (x & 0x10000000 > 0) result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128; if (x & 0x8000000 > 0) result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128; if (x & 0x4000000 > 0) result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128; if (x & 0x2000000 > 0) result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128; if (x & 0x1000000 > 0) result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128; if (x & 0x800000 > 0) result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128; if (x & 0x400000 > 0) result = result * 0x100000000002C5C85FDF477B662B26945 >> 128; if (x & 0x200000 > 0) result = result * 0x10000000000162E42FEFA3AE53369388C >> 128; if (x & 0x100000 > 0) result = result * 0x100000000000B17217F7D1D351A389D40 >> 128; if (x & 0x80000 > 0) result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128; if (x & 0x40000 > 0) result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128; if (x & 0x20000 > 0) result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128; if (x & 0x10000 > 0) result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128; if (x & 0x8000 > 0) result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128; if (x & 0x4000 > 0) result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128; if (x & 0x2000 > 0) result = result * 0x1000000000000162E42FEFA39F02B772C >> 128; if (x & 0x1000 > 0) result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128; if (x & 0x800 > 0) result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128; if (x & 0x400 > 0) result = result * 0x100000000000002C5C85FDF473DEA871F >> 128; if (x & 0x200 > 0) result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128; if (x & 0x100 > 0) result = result * 0x100000000000000B17217F7D1CF79E949 >> 128; if (x & 0x80 > 0) result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128; if (x & 0x40 > 0) result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128; if (x & 0x20 > 0) result = result * 0x100000000000000162E42FEFA39EF366F >> 128; if (x & 0x10 > 0) result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128; if (x & 0x8 > 0) result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128; if (x & 0x4 > 0) result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128; if (x & 0x2 > 0) result = result * 0x1000000000000000162E42FEFA39EF358 >> 128; if (x & 0x1 > 0) result = result * 0x10000000000000000B17217F7D1CF79AB >> 128; result >>= uint256 (int256 (63 - (x >> 64))); require (result <= uint256 (int256 (MAX_64x64))); return int128 (int256 (result)); } } /** * Calculate natural exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp (int128 x) internal pure returns (int128) { unchecked { require (x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow return exp_2 ( int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128)); } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return unsigned 64.64-bit fixed point number */ function divuu (uint256 x, uint256 y) private pure returns (uint128) { unchecked { require (y != 0); uint256 result; if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) result = (x << 64) / y; else { uint256 msb = 192; uint256 xc = x >> 192; if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1); require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 hi = result * (y >> 128); uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 xh = x >> 192; uint256 xl = x << 64; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here lo = hi << 128; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here assert (xh == hi >> 128); result += xl / y; } require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return uint128 (result); } } /** * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer * number. * * @param x unsigned 256-bit integer number * @return unsigned 128-bit integer number */ function sqrtu (uint256 x) private pure returns (uint128) { unchecked { if (x == 0) return 0; else { uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x4) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough uint256 r1 = x / r; return uint128 (r < r1 ? r : r1); } } } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; interface IProxyManager { function getPoolList() external view returns (address[] memory); }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; /** * @title Premia Exchange Helper * @dev deployed standalone and referenced by internal functions * @dev do NOT set approval to this contract! */ interface IExchangeHelper { /** * @notice perform arbitrary swap transaction * @param sourceToken source token to pull into this address * @param targetToken target token to buy * @param sourceTokenAmount amount of source token to start the trade * @param callee exchange address to call to execute the trade. * @param allowanceTarget address for which to set allowance for the trade * @param data calldata to execute the trade * @param refundAddress address that un-used source token goes to * @return amountOut quantity of targetToken yielded by swap */ function swapWithToken( address sourceToken, address targetToken, uint256 sourceTokenAmount, address callee, address allowanceTarget, bytes calldata data, address refundAddress ) external returns (uint256 amountOut); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ILayerZeroUserApplicationConfig} from "./ILayerZeroUserApplicationConfig.sol"; interface ILayerZeroEndpoint is ILayerZeroUserApplicationConfig { /** * @notice send a LayerZero message to the specified address at a LayerZero endpoint. * @param dstChainId - the destination chain identifier * @param destination - the address on destination chain (in bytes). address length/format may vary by chains * @param payload - a custom bytes payload to send to the destination contract * @param refundAddress - if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address * @param zroPaymentAddress - the address of the ZRO token holder who would pay for the transaction * @param adapterParams - parameters for custom functionality. e.g. receive airdropped native gas from the relayer on destination */ function send( uint16 dstChainId, bytes calldata destination, bytes calldata payload, address payable refundAddress, address zroPaymentAddress, bytes calldata adapterParams ) external payable; /** * @notice used by the messaging library to publish verified payload * @param srcChainId - the source chain identifier * @param srcAddress - the source contract (as bytes) at the source chain * @param dstAddress - the address on destination chain * @param nonce - the unbound message ordering nonce * @param gasLimit - the gas limit for external contract execution * @param payload - verified payload to send to the destination contract */ function receivePayload( uint16 srcChainId, bytes calldata srcAddress, address dstAddress, uint64 nonce, uint256 gasLimit, bytes calldata payload ) external; /* * @notice get the inboundNonce of a lzApp from a source chain which could be EVM or non-EVM chain * @param srcChainId - the source chain identifier * @param srcAddress - the source chain contract address */ function getInboundNonce( uint16 srcChainId, bytes calldata srcAddress ) external view returns (uint64); /* * @notice get the outboundNonce from this source chain which, consequently, is always an EVM * @param srcAddress - the source chain contract address */ function getOutboundNonce( uint16 dstChainId, address srcAddress ) external view returns (uint64); /* * @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery * @param dstChainId - the destination chain identifier * @param userApplication - the user app address on this EVM chain * @param payload - the custom message to send over LayerZero * @param payInZRO - if false, user app pays the protocol fee in native token * @param adapterParam - parameters for the adapter service, e.g. send some dust native token to dstChain */ function estimateFees( uint16 dstChainId, address userApplication, bytes calldata payload, bool payInZRO, bytes calldata adapterParam ) external view returns (uint256 nativeFee, uint256 zroFee); /* * @notice get this Endpoint's immutable source identifier */ function getChainId() external view returns (uint16); /* * @notice the interface to retry failed message on this Endpoint destination * @param srcChainId - the source chain identifier * @param srcAddress - the source chain contract address * @param payload - the payload to be retried */ function retryPayload( uint16 srcChainId, bytes calldata srcAddress, bytes calldata payload ) external; /* * @notice query if any STORED payload (message blocking) at the endpoint. * @param srcChainId - the source chain identifier * @param srcAddress - the source chain contract address */ function hasStoredPayload( uint16 srcChainId, bytes calldata srcAddress ) external view returns (bool); /* * @notice query if the libraryAddress is valid for sending msgs. * @param userApplication - the user app address on this EVM chain */ function getSendLibraryAddress( address userApplication ) external view returns (address); /* * @notice query if the libraryAddress is valid for receiving msgs. * @param userApplication - the user app address on this EVM chain */ function getReceiveLibraryAddress( address userApplication ) external view returns (address); /* * @notice query if the non-reentrancy guard for send() is on * @return true if the guard is on. false otherwise */ function isSendingPayload() external view returns (bool); /* * @notice query if the non-reentrancy guard for receive() is on * @return true if the guard is on. false otherwise */ function isReceivingPayload() external view returns (bool); /* * @notice get the configuration of the LayerZero messaging library of the specified version * @param version - messaging library version * @param chainId - the chainId for the pending config change * @param userApplication - the contract address of the user application * @param configType - type of configuration. every messaging library has its own convention. */ function getConfig( uint16 version, uint16 chainId, address userApplication, uint256 configType ) external view returns (bytes memory); /* * @notice get the send() LayerZero messaging library version * @param userApplication - the contract address of the user application */ function getSendVersion( address userApplication ) external view returns (uint16); /* * @notice get the lzReceive() LayerZero messaging library version * @param userApplication - the contract address of the user application */ function getReceiveVersion( address userApplication ) external view returns (uint16); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ILayerZeroReceiver { /* * @notice LayerZero endpoint will invoke this function to deliver the message on the destination * @param srcChainId - the source endpoint identifier * @param srcAddress - the source sending contract address from the source chain * @param nonce - the ordered message nonce * @param payload - the signed payload is the UA bytes has encoded to be sent */ function lzReceive( uint16 srcChainId, bytes calldata srcAddress, uint64 nonce, bytes calldata payload ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ILayerZeroUserApplicationConfig { /* * @notice set the configuration of the LayerZero messaging library of the specified version * @param version - messaging library version * @param chainId - the chainId for the pending config change * @param configType - type of configuration. every messaging library has its own convention. * @param config - configuration in the bytes. can encode arbitrary content. */ function setConfig( uint16 version, uint16 chainId, uint256 configType, bytes calldata config ) external; /* * @notice set the send() LayerZero messaging library version to version * @param version - new messaging library version */ function setSendVersion(uint16 version) external; /* * @notice set the lzReceive() LayerZero messaging library version to version * @param version - new messaging library version */ function setReceiveVersion(uint16 version) external; /* * @notice Only when the UA needs to resume the message flow in blocking mode and clear the stored payload * @param srcChainId - the chainId of the source chain * @param srcAddress - the contract address of the source contract at the source chain */ function forceResumeReceive( uint16 srcChainId, bytes calldata srcAddress ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {OwnableInternal} from "@solidstate/contracts/access/ownable/OwnableInternal.sol"; import {ILayerZeroReceiver} from "../interfaces/ILayerZeroReceiver.sol"; import {ILayerZeroUserApplicationConfig} from "../interfaces/ILayerZeroUserApplicationConfig.sol"; import {ILayerZeroEndpoint} from "../interfaces/ILayerZeroEndpoint.sol"; import {LzAppStorage} from "./LzAppStorage.sol"; import {BytesLib} from "../util/BytesLib.sol"; /* * a generic LzReceiver implementation */ abstract contract LzApp is OwnableInternal, ILayerZeroReceiver, ILayerZeroUserApplicationConfig { using BytesLib for bytes; ILayerZeroEndpoint public immutable lzEndpoint; // event SetPrecrime(address precrime); event SetTrustedRemoteAddress(uint16 _remoteChainId, bytes _remoteAddress); error LzApp__InvalidEndpointCaller(); error LzApp__InvalidSource(); error LzApp__NotTrustedSource(); error LzApp__NoTrustedPathRecord(); constructor(address endpoint) { lzEndpoint = ILayerZeroEndpoint(endpoint); } /** * @inheritdoc ILayerZeroReceiver */ function lzReceive( uint16 srcChainId, bytes memory srcAddress, uint64 nonce, bytes memory payload ) public virtual { // lzReceive must be called by the endpoint for security if (msg.sender != address(lzEndpoint)) revert LzApp__InvalidEndpointCaller(); // if will still block the message pathway from (srcChainId, srcAddress). should not receive message from untrusted remote. if (!_isTrustedRemote(srcChainId, srcAddress)) revert LzApp__InvalidSource(); _blockingLzReceive(srcChainId, srcAddress, nonce, payload); } // abstract function - the default behaviour of LayerZero is blocking. See: NonblockingLzApp if you dont need to enforce ordered messaging function _blockingLzReceive( uint16 srcChainId, bytes memory srcAddress, uint64 nonce, bytes memory payload ) internal virtual; function _lzSend( uint16 dstChainId, bytes memory payload, address payable refundAddress, address zroPaymentAddress, bytes memory adapterParams, uint256 nativeFee ) internal virtual { bytes memory trustedRemote = LzAppStorage.layout().trustedRemote[ dstChainId ]; if (trustedRemote.length == 0) revert LzApp__NotTrustedSource(); lzEndpoint.send{value: nativeFee}( dstChainId, trustedRemote, payload, refundAddress, zroPaymentAddress, adapterParams ); } //---------------------------UserApplication config---------------------------------------- function getConfig( uint16 version, uint16 chainId, address, uint256 configType ) external view returns (bytes memory) { return lzEndpoint.getConfig(version, chainId, address(this), configType); } /** * @inheritdoc ILayerZeroUserApplicationConfig */ function setConfig( uint16 version, uint16 chainId, uint256 configType, bytes calldata config ) external onlyOwner { lzEndpoint.setConfig(version, chainId, configType, config); } /** * @inheritdoc ILayerZeroUserApplicationConfig */ function setSendVersion(uint16 version) external onlyOwner { lzEndpoint.setSendVersion(version); } /** * @inheritdoc ILayerZeroUserApplicationConfig */ function setReceiveVersion(uint16 version) external onlyOwner { lzEndpoint.setReceiveVersion(version); } /** * @inheritdoc ILayerZeroUserApplicationConfig */ function forceResumeReceive( uint16 srcChainId, bytes calldata srcAddress ) external onlyOwner { lzEndpoint.forceResumeReceive(srcChainId, srcAddress); } function setTrustedRemoteAddress( uint16 remoteChainId, bytes calldata remoteAddress ) external onlyOwner { LzAppStorage.layout().trustedRemote[remoteChainId] = abi.encodePacked( remoteAddress, address(this) ); emit SetTrustedRemoteAddress(remoteChainId, remoteAddress); } function getTrustedRemoteAddress( uint16 _remoteChainId ) external view returns (bytes memory) { bytes memory path = LzAppStorage.layout().trustedRemote[_remoteChainId]; if (path.length == 0) revert LzApp__NoTrustedPathRecord(); return path.slice(0, path.length - 20); // the last 20 bytes should be address(this) } // function setPrecrime(address _precrime) external onlyOwner { // LzAppStorage.layout().precrime = _precrime; // emit SetPrecrime(_precrime); // } //--------------------------- VIEW FUNCTION ---------------------------------------- function isTrustedRemote( uint16 srcChainId, bytes memory srcAddress ) external view returns (bool) { return _isTrustedRemote(srcChainId, srcAddress); } function _isTrustedRemote( uint16 srcChainId, bytes memory srcAddress ) internal view returns (bool) { bytes memory trustedRemote = LzAppStorage.layout().trustedRemote[ srcChainId ]; return srcAddress.length == trustedRemote.length && trustedRemote.length > 0 && keccak256(trustedRemote) == keccak256(srcAddress); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library LzAppStorage { bytes32 internal constant STORAGE_SLOT = keccak256("premia.contracts.storage.LzApp"); struct Layout { mapping(uint16 => bytes) trustedRemote; address precrime; } function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {LzApp} from "./LzApp.sol"; import {NonblockingLzAppStorage} from "./NonblockingLzAppStorage.sol"; import {ExcessivelySafeCall} from "../util/ExcessivelySafeCall.sol"; /* * the default LayerZero messaging behaviour is blocking, i.e. any failed message will block the channel * this abstract class try-catch all fail messages and store locally for future retry. hence, non-blocking * NOTE: if the srcAddress is not configured properly, it will still block the message pathway from (srcChainId, srcAddress) */ abstract contract NonblockingLzApp is LzApp { using ExcessivelySafeCall for address; error NonblockingLzApp__CallerNotLzApp(); error NonblockingLzApp__InvalidPayload(); error NonblockingLzApp__NoStoredMessage(); constructor(address endpoint) LzApp(endpoint) {} event MessageFailed( uint16 srcChainId, bytes srcAddress, uint64 nonce, bytes payload, bytes reason ); event RetryMessageSuccess( uint16 srcChainId, bytes srcAddress, uint64 nonce, bytes32 payloadHash ); // overriding the virtual function in LzReceiver function _blockingLzReceive( uint16 srcChainId, bytes memory srcAddress, uint64 nonce, bytes memory payload ) internal virtual override { (bool success, bytes memory reason) = address(this).excessivelySafeCall( gasleft(), 150, abi.encodeWithSelector( this.nonblockingLzReceive.selector, srcChainId, srcAddress, nonce, payload ) ); // try-catch all errors/exceptions if (!success) { NonblockingLzAppStorage.layout().failedMessages[srcChainId][ srcAddress ][nonce] = keccak256(payload); emit MessageFailed(srcChainId, srcAddress, nonce, payload, reason); } } function nonblockingLzReceive( uint16 srcChainId, bytes memory srcAddress, uint64 nonce, bytes memory payload ) public virtual { // only internal transaction if (msg.sender != address(this)) revert NonblockingLzApp__CallerNotLzApp(); _nonblockingLzReceive(srcChainId, srcAddress, nonce, payload); } // override this function function _nonblockingLzReceive( uint16 srcChainId, bytes memory srcAddress, uint64 nonce, bytes memory payload ) internal virtual; function retryMessage( uint16 srcChainId, bytes memory srcAddress, uint64 nonce, bytes memory payload ) public payable virtual { NonblockingLzAppStorage.Layout storage l = NonblockingLzAppStorage .layout(); // assert there is message to retry bytes32 payloadHash = l.failedMessages[srcChainId][srcAddress][nonce]; if (payloadHash == bytes32(0)) revert NonblockingLzApp__NoStoredMessage(); if (keccak256(payload) != payloadHash) revert NonblockingLzApp__InvalidPayload(); // clear the stored message delete l.failedMessages[srcChainId][srcAddress][nonce]; // execute the message. revert if it fails again _nonblockingLzReceive(srcChainId, srcAddress, nonce, payload); emit RetryMessageSuccess(srcChainId, srcAddress, nonce, payloadHash); } function failedMessages( uint16 srcChainId, bytes memory srcAddress, uint64 nonce ) external view returns (bytes32) { return NonblockingLzAppStorage.layout().failedMessages[srcChainId][ srcAddress ][nonce]; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library NonblockingLzAppStorage { bytes32 internal constant STORAGE_SLOT = keccak256("premia.contracts.storage.NonblockingLzApp"); struct Layout { mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) failedMessages; } function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IOFTCore} from "./IOFTCore.sol"; import {ISolidStateERC20} from "@solidstate/contracts/token/ERC20/ISolidStateERC20.sol"; /** * @dev Interface of the OFT standard */ interface IOFT is IOFTCore, ISolidStateERC20 { error OFT_InsufficientAllowance(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IERC20} from "@solidstate/contracts/interfaces/IERC20.sol"; import {IERC165} from "@solidstate/contracts/interfaces/IERC165.sol"; /** * @dev Interface of the IOFT core standard */ interface IOFTCore is IERC165 { /** * @dev estimate send token `tokenId` to (`dstChainId`, `toAddress`) * dstChainId - L0 defined chain id to send tokens too * toAddress - dynamic bytes array which contains the address to whom you are sending tokens to on the dstChain * amount - amount of the tokens to transfer * useZro - indicates to use zro to pay L0 fees * adapterParam - flexible bytes array to indicate messaging adapter services in L0 */ function estimateSendFee( uint16 dstChainId, bytes calldata toAddress, uint256 amount, bool useZro, bytes calldata adapterParams ) external view returns (uint256 nativeFee, uint256 zroFee); /** * @dev send `amount` amount of token to (`dstChainId`, `toAddress`) from `from` * `from` the owner of token * `dstChainId` the destination chain identifier * `toAddress` can be any size depending on the `dstChainId`. * `amount` the quantity of tokens in wei * `refundAddress` the address LayerZero refunds if too much message fee is sent * `zroPaymentAddress` set to address(0x0) if not paying in ZRO (LayerZero Token) * `adapterParams` is a flexible bytes array to indicate messaging adapter services */ function sendFrom( address from, uint16 dstChainId, bytes calldata toAddress, uint256 amount, address payable refundAddress, address zroPaymentAddress, bytes calldata adapterParams ) external payable; /** * @dev returns the circulating amount of tokens on current chain */ function circulatingSupply() external view returns (uint256); /** * @dev Emitted when `amount` tokens are moved from the `sender` to (`dstChainId`, `toAddress`) * `nonce` is the outbound nonce */ event SendToChain( address indexed sender, uint16 indexed dstChainId, bytes indexed toAddress, uint256 amount ); /** * @dev Emitted when `amount` tokens are received from `srcChainId` into the `toAddress` on the local chain. * `nonce` is the inbound nonce. */ event ReceiveFromChain( uint16 indexed srcChainId, bytes indexed srcAddress, address indexed toAddress, uint256 amount ); event SetUseCustomAdapterParams(bool _useCustomAdapterParams); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ERC20Base, ERC20BaseStorage} from "@solidstate/contracts/token/ERC20/base/ERC20Base.sol"; import {SolidStateERC20} from "@solidstate/contracts/token/ERC20/SolidStateERC20.sol"; import {IERC20} from "@solidstate/contracts/interfaces/IERC20.sol"; import {IERC165} from "@solidstate/contracts/interfaces/IERC165.sol"; import {OFTCore} from "./OFTCore.sol"; import {IOFT} from "./IOFT.sol"; // override decimal() function is needed contract OFT is OFTCore, SolidStateERC20, IOFT { constructor(address lzEndpoint) OFTCore(lzEndpoint) {} function circulatingSupply() public view virtual override returns (uint256) { return _totalSupply(); } function _debitFrom( address from, uint16, bytes memory, uint256 amount ) internal virtual override { address spender = msg.sender; if (from != spender) { unchecked { mapping(address => uint256) storage allowances = ERC20BaseStorage.layout().allowances[ spender ]; uint256 allowance = allowances[spender]; if (amount > allowance) revert OFT_InsufficientAllowance(); _approve( from, spender, allowances[spender] = allowance - amount ); } } _burn(from, amount); } function _creditTo( uint16, address toAddress, uint256 amount ) internal virtual override { _mint(toAddress, amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {NonblockingLzApp} from "../../lzApp/NonblockingLzApp.sol"; import {IOFTCore} from "./IOFTCore.sol"; import {ERC165Base, IERC165} from "@solidstate/contracts/introspection/ERC165/base/ERC165Base.sol"; import {BytesLib} from "../../util/BytesLib.sol"; abstract contract OFTCore is NonblockingLzApp, ERC165Base, IOFTCore { using BytesLib for bytes; // packet type uint16 public constant PT_SEND = 0; constructor(address lzEndpoint) NonblockingLzApp(lzEndpoint) {} function estimateSendFee( uint16 dstChainId, bytes memory toAddress, uint256 amount, bool useZro, bytes memory adapterParams ) public view virtual override returns (uint256 nativeFee, uint256 zroFee) { // mock the payload for send() bytes memory payload = abi.encode( PT_SEND, abi.encodePacked(msg.sender), toAddress, amount ); return lzEndpoint.estimateFees( dstChainId, address(this), payload, useZro, adapterParams ); } function sendFrom( address from, uint16 dstChainId, bytes memory toAddress, uint256 amount, address payable refundAddress, address zroPaymentAddress, bytes memory adapterParams ) public payable virtual override { _send( from, dstChainId, toAddress, amount, refundAddress, zroPaymentAddress, adapterParams ); } function _nonblockingLzReceive( uint16 srcChainId, bytes memory srcAddress, uint64 nonce, bytes memory payload ) internal virtual override { uint16 packetType; assembly { packetType := mload(add(payload, 32)) } if (packetType == PT_SEND) { _sendAck(srcChainId, srcAddress, nonce, payload); } else { revert("OFTCore: unknown packet type"); } } function _send( address from, uint16 dstChainId, bytes memory toAddress, uint256 amount, address payable refundAddress, address zroPaymentAddress, bytes memory adapterParams ) internal virtual { _debitFrom(from, dstChainId, toAddress, amount); bytes memory payload = abi.encode( PT_SEND, abi.encodePacked(from), toAddress, amount ); _lzSend( dstChainId, payload, refundAddress, zroPaymentAddress, adapterParams, msg.value ); emit SendToChain(from, dstChainId, toAddress, amount); } function _sendAck( uint16 srcChainId, bytes memory, uint64, bytes memory payload ) internal virtual { (, bytes memory from, bytes memory toAddressBytes, uint256 amount) = abi .decode(payload, (uint16, bytes, bytes, uint256)); address to = toAddressBytes.toAddress(0); _creditTo(srcChainId, to, amount); emit ReceiveFromChain(srcChainId, from, to, amount); } function _debitFrom( address from, uint16 dstChainId, bytes memory toAddress, uint256 amount ) internal virtual; function _creditTo( uint16 srcChainId, address toAddress, uint256 amount ) internal virtual; }
// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity >=0.8.0 <0.9.0; library BytesLib { error BytesLib__Overflow(); error BytesLib__OutOfBounds(); function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore( 0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. ) ) } return tempBytes; } function concatStorage( bytes storage _preBytes, bytes memory _postBytes ) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes.slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div( and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2 ) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes.slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and( fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 ), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { if (_length + 31 < _length) revert BytesLib__Overflow(); if (_bytes.length < _start + _length) revert BytesLib__OutOfBounds(); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add( add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)) ) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add( add( add(_bytes, lengthmod), mul(0x20, iszero(lengthmod)) ), _start ) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress( bytes memory _bytes, uint256 _start ) internal pure returns (address) { if (_bytes.length < _start + 20) revert BytesLib__OutOfBounds(); address tempAddress; assembly { tempAddress := div( mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000 ) } return tempAddress; } function toUint8( bytes memory _bytes, uint256 _start ) internal pure returns (uint8) { if (_bytes.length < _start + 1) revert BytesLib__OutOfBounds(); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16( bytes memory _bytes, uint256 _start ) internal pure returns (uint16) { if (_bytes.length < _start + 2) revert BytesLib__OutOfBounds(); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32( bytes memory _bytes, uint256 _start ) internal pure returns (uint32) { if (_bytes.length < _start + 4) revert BytesLib__OutOfBounds(); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64( bytes memory _bytes, uint256 _start ) internal pure returns (uint64) { if (_bytes.length < _start + 8) revert BytesLib__OutOfBounds(); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96( bytes memory _bytes, uint256 _start ) internal pure returns (uint96) { if (_bytes.length < _start + 12) revert BytesLib__OutOfBounds(); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128( bytes memory _bytes, uint256 _start ) internal pure returns (uint128) { if (_bytes.length < _start + 16) revert BytesLib__OutOfBounds(); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256( bytes memory _bytes, uint256 _start ) internal pure returns (uint256) { if (_bytes.length < _start + 32) revert BytesLib__OutOfBounds(); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32( bytes memory _bytes, uint256 _start ) internal pure returns (bytes32) { if (_bytes.length < _start + 32) revert BytesLib__OutOfBounds(); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equalStorage( bytes storage _preBytes, bytes memory _postBytes ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). let slength := div( and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2 ) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for { } eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.7.6; library ExcessivelySafeCall { uint256 constant LOW_28_MASK = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeCall( address _target, uint256 _gas, uint16 _maxCopy, bytes memory _calldata ) internal returns (bool, bytes memory) { // set up for assembly call uint256 _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := call( _gas, // gas _target, // recipient 0, // ether value add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeStaticCall( address _target, uint256 _gas, uint16 _maxCopy, bytes memory _calldata ) internal view returns (bool, bytes memory) { // set up for assembly call uint256 _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := staticcall( _gas, // gas _target, // recipient add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /** * @notice Swaps function selectors in encoded contract calls * @dev Allows reuse of encoded calldata for functions with identical * argument types but different names. It simply swaps out the first 4 bytes * for the new selector. This function modifies memory in place, and should * only be used with caution. * @param _newSelector The new 4-byte selector * @param _buf The encoded contract args */ function swapSelector( bytes4 _newSelector, bytes memory _buf ) internal pure { require(_buf.length >= 4); uint256 _mask = LOW_28_MASK; assembly { // load the first word of let _word := mload(add(_buf, 0x20)) // mask out the top 4 bytes // /x _word := and(_word, _mask) _word := or(_newSelector, _word) mstore(add(_buf, 0x20), _word) } } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; import {PremiaStakingStorage} from "./PremiaStakingStorage.sol"; import {IOFT} from "../layerZero/token/oft/IOFT.sol"; import {IERC2612} from "@solidstate/contracts/token/ERC20/permit/IERC2612.sol"; // IERC20Metadata inheritance not possible due to linearization issue interface IPremiaStaking is IERC2612, IOFT { error PremiaStaking__CantTransfer(); error PremiaStaking__ExcessiveStakePeriod(); error PremiaStaking__InsufficientSwapOutput(); error PremiaStaking__NoPendingWithdrawal(); error PremiaStaking__NotEnoughLiquidity(); error PremiaStaking__PeriodTooShort(); error PremiaStaking__StakeLocked(); error PremiaStaking__StakeNotLocked(); error PremiaStaking__WithdrawalStillPending(); event Stake( address indexed user, uint256 amount, uint64 stakePeriod, uint64 lockedUntil ); event Unstake( address indexed user, uint256 amount, uint256 fee, uint256 startDate ); event Harvest(address indexed user, uint256 amount); event EarlyUnstakeRewardCollected(address indexed user, uint256 amount); event Withdraw(address indexed user, uint256 amount); event RewardsAdded(uint256 amount); struct StakeLevel { uint256 amount; // Amount to stake uint256 discountBPS; // Discount when amount is reached } struct SwapArgs { //min amount out to be used to purchase uint256 amountOutMin; // exchange address to call to execute the trade address callee; // address for which to set allowance for the trade address allowanceTarget; // data to execute the trade bytes data; // address to which refund excess tokens address refundAddress; } event BridgeLock( address indexed user, uint64 stakePeriod, uint64 lockedUntil ); event UpdateLock( address indexed user, uint64 oldStakePeriod, uint64 newStakePeriod ); /** * @notice Returns the reward token address * @return The reward token address */ function getRewardToken() external view returns (address); /** * @notice add premia tokens as available tokens to be distributed as rewards * @param amount amount of premia tokens to add as rewards */ function addRewards(uint256 amount) external; /** * @notice get amount of tokens that have not yet been distributed as rewards * @return rewards amount of tokens not yet distributed as rewards * @return unstakeRewards amount of PREMIA not yet claimed from early unstake fees */ function getAvailableRewards() external view returns (uint256 rewards, uint256 unstakeRewards); /** * @notice get pending amount of tokens to be distributed as rewards to stakers * @return amount of tokens pending to be distributed as rewards */ function getPendingRewards() external view returns (uint256); /** * @notice get pending withdrawal data of a user * @return amount pending withdrawal amount * @return startDate start timestamp of withdrawal * @return unlockDate timestamp at which withdrawal becomes available */ function getPendingWithdrawal( address user ) external view returns (uint256 amount, uint256 startDate, uint256 unlockDate); /** * @notice get the amount of PREMIA available for withdrawal * @return amount of PREMIA available for withdrawal */ function getAvailablePremiaAmount() external view returns (uint256); /** * @notice Stake using IERC2612 permit * @param amount The amount of xPremia to stake * @param period The lockup period (in seconds) * @param deadline Deadline after which permit will fail * @param v V * @param r R * @param s S */ function stakeWithPermit( uint256 amount, uint64 period, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @notice Lockup xPremia for protocol fee discounts * Longer period of locking will apply a multiplier on the amount staked, in the fee discount calculation * @param amount The amount of xPremia to stake * @param period The lockup period (in seconds) */ function stake(uint256 amount, uint64 period) external; /** * @notice update vxPremia lock * @param period The new lockup period (in seconds) */ function updateLock(uint64 period) external; /** * @notice harvest rewards, convert to PREMIA using exchange helper, and stake * @param s swap arguments * @param stakePeriod The lockup period (in seconds) */ function harvestAndStake( IPremiaStaking.SwapArgs memory s, uint64 stakePeriod ) external; /** * @notice Harvest rewards directly to user wallet */ function harvest() external; /** * @notice Get pending rewards amount, including pending pool update * @param user User for which to calculate pending rewards * @return reward amount of pending rewards from protocol fees (in REWARD_TOKEN) * @return unstakeReward amount of pending rewards from early unstake fees (in PREMIA) */ function getPendingUserRewards( address user ) external view returns (uint256 reward, uint256 unstakeReward); /** * @notice unstake tokens before end of the lock period, for a fee * @param amount the amount of vxPremia to unstake */ function earlyUnstake(uint256 amount) external; /** * @notice get early unstake fee for given user * @param user address of the user * @return feePercentage % fee to pay for early unstake (1e4 = 100%) */ function getEarlyUnstakeFeeBPS( address user ) external view returns (uint256 feePercentage); /** * @notice Initiate the withdrawal process by burning xPremia, starting the delay period * @param amount quantity of xPremia to unstake */ function startWithdraw(uint256 amount) external; /** * @notice Withdraw underlying premia */ function withdraw() external; ////////// // View // ////////// /** * Calculate the stake amount of a user, after applying the bonus from the lockup period chosen * @param user The user from which to query the stake amount * @return The user stake amount after applying the bonus */ function getUserPower(address user) external view returns (uint256); /** * Return the total power across all users (applying the bonus from lockup period chosen) * @return The total power across all users */ function getTotalPower() external view returns (uint256); /** * @notice Calculate the % of fee discount for user, based on his stake * @param user The _user for which the discount is for * @return Percentage of protocol fee discount (in basis point) * Ex : 1000 = 10% fee discount */ function getDiscountBPS(address user) external view returns (uint256); /** * @notice Get stake levels * @return Stake levels * Ex : 2500 = -25% */ function getStakeLevels() external returns (StakeLevel[] memory); /** * @notice Get stake period multiplier * @param period The duration (in seconds) for which tokens are locked * @return The multiplier for this staking period * Ex : 20000 = x2 */ function getStakePeriodMultiplierBPS( uint256 period ) external returns (uint256); /** * @notice Get staking infos of a user * @param user The user address for which to get staking infos * @return The staking infos of the user */ function getUserInfo( address user ) external view returns (PremiaStakingStorage.UserInfo memory); }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; import {VxPremiaStorage} from "./VxPremiaStorage.sol"; import {IPremiaStaking} from "./IPremiaStaking.sol"; interface IVxPremia is IPremiaStaking { error VxPremia__InvalidPoolAddress(); error VxPremia__InvalidVoteTarget(); error VxPremia__NotEnoughVotingPower(); event AddVote( address indexed voter, VxPremiaStorage.VoteVersion indexed version, bytes target, uint256 amount ); event RemoveVote( address indexed voter, VxPremiaStorage.VoteVersion indexed version, bytes target, uint256 amount ); /** * @notice get total votes for specific pools * @param version version of target (used to know how to decode data) * @param target ABI encoded target of the votes * @return total votes for specific pool */ function getPoolVotes( VxPremiaStorage.VoteVersion version, bytes memory target ) external view returns (uint256); /** * @notice get votes of user * @param user user from which to get votes * @return votes of user */ function getUserVotes( address user ) external view returns (VxPremiaStorage.Vote[] memory); /** * @notice add or remove votes, in the limit of the user voting power * @param votes votes to cast */ function castVotes(VxPremiaStorage.Vote[] memory votes) external; }
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; import {AddressUtils} from "@solidstate/contracts/utils/AddressUtils.sol"; import {Math} from "@solidstate/contracts/utils/Math.sol"; import {IERC20} from "@solidstate/contracts/interfaces/IERC20.sol"; import {IERC2612} from "@solidstate/contracts/token/ERC20/permit/IERC2612.sol"; import {SafeERC20} from "@solidstate/contracts/utils/SafeERC20.sol"; import {ABDKMath64x64} from "abdk-libraries-solidity/ABDKMath64x64.sol"; import {IExchangeHelper} from "../interfaces/IExchangeHelper.sol"; import {IPremiaStaking} from "./IPremiaStaking.sol"; import {PremiaStakingStorage} from "./PremiaStakingStorage.sol"; import {OFT} from "../layerZero/token/oft/OFT.sol"; import {OFTCore} from "../layerZero/token/oft/OFTCore.sol"; import {IOFTCore} from "../layerZero/token/oft/IOFTCore.sol"; import {BytesLib} from "../layerZero/util/BytesLib.sol"; contract PremiaStaking is IPremiaStaking, OFT { using SafeERC20 for IERC20; using ABDKMath64x64 for int128; using AddressUtils for address; using BytesLib for bytes; address internal immutable PREMIA; address internal immutable REWARD_TOKEN; address internal immutable EXCHANGE_HELPER; int128 internal constant ONE_64x64 = 0x10000000000000000; int128 internal constant DECAY_RATE_64x64 = 0x487a423b63e; // 2.7e-7 -> Distribute around half of the current balance over a month uint256 internal constant INVERSE_BASIS_POINT = 1e4; uint64 internal constant MAX_PERIOD = 4 * 365 days; uint256 internal constant ACC_REWARD_PRECISION = 1e30; uint256 internal constant MAX_CONTRACT_DISCOUNT = 3000; // -30% uint256 internal constant WITHDRAWAL_DELAY = 10 days; struct UpdateArgsInternal { address user; uint256 balance; uint256 oldPower; uint256 newPower; uint256 reward; uint256 unstakeReward; } constructor( address lzEndpoint, address premia, address rewardToken, address exchangeHelper ) OFT(lzEndpoint) { PREMIA = premia; REWARD_TOKEN = rewardToken; EXCHANGE_HELPER = exchangeHelper; } function _beforeTokenTransfer( address from, address to, uint256 ) internal virtual override { if (from == address(0) || to == address(0)) return; revert PremiaStaking__CantTransfer(); } /** * @inheritdoc IPremiaStaking */ function getRewardToken() external view returns (address) { return REWARD_TOKEN; } function estimateSendFee( uint16 dstChainId, bytes memory toAddress, uint256 amount, bool useZro, bytes memory adapterParams ) public view virtual override(OFTCore, IOFTCore) returns (uint256 nativeFee, uint256 zroFee) { // Convert bytes to address address to; assembly { to := mload(add(toAddress, 32)) } PremiaStakingStorage.UserInfo storage u = PremiaStakingStorage .layout() .userInfo[to]; return lzEndpoint.estimateFees( dstChainId, address(this), abi.encode(PT_SEND, to, amount, u.stakePeriod, u.lockedUntil), useZro, adapterParams ); } function _send( address from, uint16 dstChainId, bytes memory, uint256 amount, address payable refundAddress, address zroPaymentAddress, bytes memory adapterParams ) internal virtual override { _updateRewards(); _beforeUnstake(from, amount); PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); PremiaStakingStorage.UserInfo storage u = l.userInfo[from]; UpdateArgsInternal memory args = _getInitialUpdateArgsInternal( l, u, from ); bytes memory toAddress = abi.encodePacked(from); _debitFrom(from, dstChainId, toAddress, amount); args.newPower = _calculateUserPower( args.balance - amount + args.unstakeReward, u.stakePeriod ); _updateUser(l, u, args); _lzSend( dstChainId, abi.encode( PT_SEND, toAddress, amount, u.stakePeriod, u.lockedUntil ), refundAddress, zroPaymentAddress, adapterParams, msg.value ); emit SendToChain(from, dstChainId, toAddress, amount); } function _sendAck( uint16 srcChainId, bytes memory srcAddress, uint64, bytes memory payload ) internal virtual override { ( , bytes memory toAddressBytes, uint256 amount, uint64 stakePeriod, uint64 lockedUntil ) = abi.decode(payload, (uint16, bytes, uint256, uint64, uint64)); address to = toAddressBytes.toAddress(0); _creditTo(to, amount, stakePeriod, lockedUntil, true); emit ReceiveFromChain(srcChainId, srcAddress, to, amount); } function _creditTo( address toAddress, uint256 amount, uint64 stakePeriod, uint64 creditLockedUntil, bool bridge ) internal { unchecked { _updateRewards(); PremiaStakingStorage.Layout storage l = PremiaStakingStorage .layout(); PremiaStakingStorage.UserInfo storage u = l.userInfo[toAddress]; UpdateArgsInternal memory args = _getInitialUpdateArgsInternal( l, u, toAddress ); uint64 lockedUntil = u.lockedUntil; uint64 lockLeft = uint64( _calculateWeightedAverage( creditLockedUntil > block.timestamp ? creditLockedUntil - block.timestamp : 0, lockedUntil > block.timestamp ? lockedUntil - block.timestamp : 0, amount + args.unstakeReward, args.balance ) ); u.lockedUntil = lockedUntil = uint64(block.timestamp) + lockLeft; u.stakePeriod = uint64( _calculateWeightedAverage( stakePeriod, u.stakePeriod, amount + args.unstakeReward, args.balance ) ); args.newPower = _calculateUserPower( args.balance + amount + args.unstakeReward, u.stakePeriod ); _mint(toAddress, amount); _updateUser(l, u, args); if (bridge) { emit BridgeLock(toAddress, u.stakePeriod, lockedUntil); } else { emit Stake(toAddress, amount, u.stakePeriod, lockedUntil); } } } /** * @inheritdoc IPremiaStaking */ function addRewards(uint256 amount) external { _updateRewards(); IERC20(REWARD_TOKEN).safeTransferFrom( msg.sender, address(this), amount ); PremiaStakingStorage.layout().availableRewards += amount; emit RewardsAdded(amount); } /** * @inheritdoc IPremiaStaking */ function getAvailableRewards() external view returns (uint256 rewards, uint256 unstakeRewards) { PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); unchecked { rewards = l.availableRewards - _getPendingRewards(); } unstakeRewards = l.availableUnstakeRewards; } /** * @inheritdoc IPremiaStaking */ function getPendingRewards() external view returns (uint256) { return _getPendingRewards(); } function _getPendingRewards() internal view returns (uint256) { PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); return l.availableRewards - _decay(l.availableRewards, l.lastRewardUpdate, block.timestamp); } function _updateRewards() internal { PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); if ( l.lastRewardUpdate == 0 || l.totalPower == 0 || l.availableRewards == 0 ) { l.lastRewardUpdate = block.timestamp; return; } uint256 pendingRewards = _getPendingRewards(); l.accRewardPerShare += (pendingRewards * ACC_REWARD_PRECISION) / l.totalPower; unchecked { l.availableRewards -= pendingRewards; } l.lastRewardUpdate = block.timestamp; } /** * @inheritdoc IPremiaStaking */ function stakeWithPermit( uint256 amount, uint64 period, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { IERC2612(PREMIA).permit( msg.sender, address(this), amount, deadline, v, r, s ); IERC20(PREMIA).safeTransferFrom(msg.sender, address(this), amount); _stake(msg.sender, amount, period); } /** * @inheritdoc IPremiaStaking */ function stake(uint256 amount, uint64 period) external { IERC20(PREMIA).safeTransferFrom(msg.sender, address(this), amount); _stake(msg.sender, amount, period); } /** * @inheritdoc IPremiaStaking */ function updateLock(uint64 period) external { if (period > MAX_PERIOD) revert PremiaStaking__ExcessiveStakePeriod(); _updateRewards(); PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); PremiaStakingStorage.UserInfo storage u = l.userInfo[msg.sender]; uint64 oldPeriod = u.stakePeriod; if (period <= oldPeriod) revert PremiaStaking__PeriodTooShort(); UpdateArgsInternal memory args = _getInitialUpdateArgsInternal( l, u, msg.sender ); unchecked { uint64 lockToAdd = period - oldPeriod; u.lockedUntil = uint64(Math.max(u.lockedUntil, block.timestamp)) + lockToAdd; u.stakePeriod = period; args.newPower = _calculateUserPower( args.balance + args.unstakeReward, period ); } _updateUser(l, u, args); emit UpdateLock(msg.sender, oldPeriod, period); } /** * @inheritdoc IPremiaStaking */ function harvestAndStake( IPremiaStaking.SwapArgs memory s, uint64 stakePeriod ) external { uint256 amountRewardToken = _harvest(msg.sender); if (amountRewardToken == 0) return; IERC20(REWARD_TOKEN).safeTransfer(EXCHANGE_HELPER, amountRewardToken); uint256 amountPremia = IExchangeHelper(EXCHANGE_HELPER).swapWithToken( REWARD_TOKEN, PREMIA, amountRewardToken, s.callee, s.allowanceTarget, s.data, s.refundAddress ); if (amountPremia < s.amountOutMin) revert PremiaStaking__InsufficientSwapOutput(); _stake(msg.sender, amountPremia, stakePeriod); } function _calculateWeightedAverage( uint256 A, uint256 B, uint256 weightA, uint256 weightB ) internal pure returns (uint256) { return (A * weightA + B * weightB) / (weightA + weightB); } function _stake( address toAddress, uint256 amount, uint64 stakePeriod ) internal { if (stakePeriod > MAX_PERIOD) revert PremiaStaking__ExcessiveStakePeriod(); unchecked { _creditTo( toAddress, amount, stakePeriod, uint64(block.timestamp) + stakePeriod, false ); } } /** * @inheritdoc IPremiaStaking */ function getPendingUserRewards( address user ) external view returns (uint256 reward, uint256 unstakeReward) { PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); PremiaStakingStorage.UserInfo storage u = l.userInfo[user]; uint256 accRewardPerShare = l.accRewardPerShare; if (l.lastRewardUpdate > 0 && l.availableRewards > 0) { accRewardPerShare += (_getPendingRewards() * ACC_REWARD_PRECISION) / l.totalPower; } uint256 power = _calculateUserPower(_balanceOf(user), u.stakePeriod); reward = u.reward + _calculateReward(accRewardPerShare, power, u.rewardDebt); unstakeReward = _calculateReward( l.accUnstakeRewardPerShare, power, u.unstakeRewardDebt ); } function harvest() external { uint256 amount = _harvest(msg.sender); IERC20(REWARD_TOKEN).safeTransfer(msg.sender, amount); } function _harvest(address account) internal returns (uint256 amount) { _updateRewards(); PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); PremiaStakingStorage.UserInfo storage u = l.userInfo[account]; UpdateArgsInternal memory args = _getInitialUpdateArgsInternal( l, u, account ); if (args.unstakeReward > 0) { args.newPower = _calculateUserPower( args.balance + args.unstakeReward, u.stakePeriod ); } else { args.newPower = args.oldPower; } _updateUser(l, u, args); amount = u.reward; u.reward = 0; emit Harvest(account, amount); } function _updateTotalPower( PremiaStakingStorage.Layout storage l, uint256 oldUserPower, uint256 newUserPower ) internal { if (newUserPower > oldUserPower) { l.totalPower += newUserPower - oldUserPower; } else if (newUserPower < oldUserPower) { l.totalPower -= oldUserPower - newUserPower; } } function _beforeUnstake(address user, uint256 amount) internal virtual {} /** * @inheritdoc IPremiaStaking */ function earlyUnstake(uint256 amount) external { PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); _startWithdraw( l, l.userInfo[msg.sender], amount, (amount * _getEarlyUnstakeFeeBPS(msg.sender)) / INVERSE_BASIS_POINT ); } /** * @inheritdoc IPremiaStaking */ function getEarlyUnstakeFeeBPS( address user ) external view returns (uint256 feePercentage) { return _getEarlyUnstakeFeeBPS(user); } function _getEarlyUnstakeFeeBPS( address user ) internal view returns (uint256 feePercentageBPS) { uint256 lockedUntil = PremiaStakingStorage .layout() .userInfo[user] .lockedUntil; if (lockedUntil <= block.timestamp) revert PremiaStaking__StakeNotLocked(); uint256 lockLeft; unchecked { lockLeft = lockedUntil - block.timestamp; feePercentageBPS = (lockLeft * 2500) / 365 days; // 25% fee per year left } if (feePercentageBPS > 7500) { feePercentageBPS = 7500; // Capped at 75% } } /** * @inheritdoc IPremiaStaking */ function startWithdraw(uint256 amount) external { PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); PremiaStakingStorage.UserInfo storage u = l.userInfo[msg.sender]; if (u.lockedUntil > block.timestamp) revert PremiaStaking__StakeLocked(); _startWithdraw(l, u, amount, 0); } function _startWithdraw( PremiaStakingStorage.Layout storage l, PremiaStakingStorage.UserInfo storage u, uint256 amount, uint256 fee ) internal { uint256 amountMinusFee; unchecked { amountMinusFee = amount - fee; } if (_getAvailablePremiaAmount() < amountMinusFee) revert PremiaStaking__NotEnoughLiquidity(); _updateRewards(); _beforeUnstake(msg.sender, amount); UpdateArgsInternal memory args = _getInitialUpdateArgsInternal( l, u, msg.sender ); _burn(msg.sender, amount); l.pendingWithdrawal += amountMinusFee; if (fee > 0) { l.accUnstakeRewardPerShare += (fee * ACC_REWARD_PRECISION) / (l.totalPower - args.oldPower); // User who early unstake doesnt collect any of the fee l.availableUnstakeRewards += fee; } args.newPower = _calculateUserPower( args.balance - amount + args.unstakeReward, u.stakePeriod ); _updateUser(l, u, args); l.withdrawals[msg.sender].amount += amountMinusFee; l.withdrawals[msg.sender].startDate = block.timestamp; emit Unstake(msg.sender, amount, fee, block.timestamp); } /** * @inheritdoc IPremiaStaking */ function withdraw() external { _updateRewards(); PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); uint256 startDate = l.withdrawals[msg.sender].startDate; if (startDate == 0) revert PremiaStaking__NoPendingWithdrawal(); unchecked { if (block.timestamp <= startDate + WITHDRAWAL_DELAY) revert PremiaStaking__WithdrawalStillPending(); } uint256 amount = l.withdrawals[msg.sender].amount; l.pendingWithdrawal -= amount; delete l.withdrawals[msg.sender]; IERC20(PREMIA).safeTransfer(msg.sender, amount); emit Withdraw(msg.sender, amount); } /** * @inheritdoc IPremiaStaking */ function getTotalPower() external view returns (uint256) { return PremiaStakingStorage.layout().totalPower; } /** * @inheritdoc IPremiaStaking */ function getUserPower(address user) external view returns (uint256) { return _calculateUserPower( _balanceOf(user), PremiaStakingStorage.layout().userInfo[user].stakePeriod ); } /** * @inheritdoc IPremiaStaking */ function getDiscountBPS(address user) external view returns (uint256) { PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); uint256 userPower = _calculateUserPower( _balanceOf(user), l.userInfo[user].stakePeriod ); // If user is a contract, we use a different formula based on % of total power owned by the contract if (user.isContract()) { // Require 50% of overall staked power for contract to have max discount if (userPower >= l.totalPower >> 1) { return MAX_CONTRACT_DISCOUNT; } else { return (userPower * MAX_CONTRACT_DISCOUNT) / (l.totalPower >> 1); } } IPremiaStaking.StakeLevel[] memory stakeLevels = _getStakeLevels(); uint256 length = stakeLevels.length; unchecked { for (uint256 i = 0; i < length; i++) { IPremiaStaking.StakeLevel memory level = stakeLevels[i]; if (userPower < level.amount) { uint256 amountPrevLevel; uint256 discountPrevLevelBPS; // If stake is lower, user is in this level, and we need to LERP with prev level to get discount value if (i > 0) { amountPrevLevel = stakeLevels[i - 1].amount; discountPrevLevelBPS = stakeLevels[i - 1].discountBPS; } else { // If this is the first level, prev level is 0 / 0 amountPrevLevel = 0; discountPrevLevelBPS = 0; } uint256 remappedDiscountBPS = level.discountBPS - discountPrevLevelBPS; uint256 remappedAmount = level.amount - amountPrevLevel; uint256 remappedPower = userPower - amountPrevLevel; uint256 levelProgressBPS = (remappedPower * INVERSE_BASIS_POINT) / remappedAmount; return discountPrevLevelBPS + ((remappedDiscountBPS * levelProgressBPS) / INVERSE_BASIS_POINT); } } // If no match found it means user is >= max possible stake, and therefore has max discount possible return stakeLevels[length - 1].discountBPS; } } /** * @inheritdoc IPremiaStaking */ function getStakeLevels() external pure returns (IPremiaStaking.StakeLevel[] memory stakeLevels) { return _getStakeLevels(); } /** * @inheritdoc IPremiaStaking */ function getStakePeriodMultiplierBPS( uint256 period ) external pure returns (uint256) { return _getStakePeriodMultiplierBPS(period); } /** * @inheritdoc IPremiaStaking */ function getUserInfo( address user ) external view returns (PremiaStakingStorage.UserInfo memory) { return PremiaStakingStorage.layout().userInfo[user]; } function getPendingWithdrawals() external view returns (uint256) { return PremiaStakingStorage.layout().pendingWithdrawal; } function getPendingWithdrawal( address user ) external view returns (uint256 amount, uint256 startDate, uint256 unlockDate) { PremiaStakingStorage.Layout storage l = PremiaStakingStorage.layout(); amount = l.withdrawals[user].amount; startDate = l.withdrawals[user].startDate; unchecked { if (startDate > 0) { unlockDate = startDate + WITHDRAWAL_DELAY; } } } function _decay( uint256 pendingRewards, uint256 oldTimestamp, uint256 newTimestamp ) internal pure returns (uint256) { return ONE_64x64 .sub(DECAY_RATE_64x64) .pow(newTimestamp - oldTimestamp) .mulu(pendingRewards); } function _getStakeLevels() internal pure returns (IPremiaStaking.StakeLevel[] memory stakeLevels) { stakeLevels = new IPremiaStaking.StakeLevel[](4); stakeLevels[0] = IPremiaStaking.StakeLevel(5000 * 1e18, 1000); // -10% stakeLevels[1] = IPremiaStaking.StakeLevel(50000 * 1e18, 2500); // -25% stakeLevels[2] = IPremiaStaking.StakeLevel(500000 * 1e18, 3500); // -35% stakeLevels[3] = IPremiaStaking.StakeLevel(2500000 * 1e18, 6000); // -60% } function _getStakePeriodMultiplierBPS( uint256 period ) internal pure returns (uint256) { unchecked { uint256 oneYear = 365 days; if (period == 0) return 2500; // x0.25 if (period >= 4 * oneYear) return 42500; // x4.25 return 2500 + (period * 1e4) / oneYear; // 0.25x + 1.0x per year lockup } } function _calculateUserPower( uint256 balance, uint64 stakePeriod ) internal pure returns (uint256) { return (balance * _getStakePeriodMultiplierBPS(stakePeriod)) / INVERSE_BASIS_POINT; } function _calculateReward( uint256 accRewardPerShare, uint256 power, uint256 rewardDebt ) internal pure returns (uint256) { return ((accRewardPerShare * power) / ACC_REWARD_PRECISION) - rewardDebt; } function _creditRewards( PremiaStakingStorage.Layout storage l, PremiaStakingStorage.UserInfo storage u, address user, uint256 reward, uint256 unstakeReward ) internal { u.reward += reward; if (unstakeReward > 0) { l.availableUnstakeRewards -= unstakeReward; _mint(user, unstakeReward); emit EarlyUnstakeRewardCollected(user, unstakeReward); } } function _getInitialUpdateArgsInternal( PremiaStakingStorage.Layout storage l, PremiaStakingStorage.UserInfo storage u, address user ) internal view returns (UpdateArgsInternal memory) { UpdateArgsInternal memory args; args.user = user; args.balance = _balanceOf(user); if (args.balance > 0) { args.oldPower = _calculateUserPower(args.balance, u.stakePeriod); } args.reward = _calculateReward( l.accRewardPerShare, args.oldPower, u.rewardDebt ); args.unstakeReward = _calculateReward( l.accUnstakeRewardPerShare, args.oldPower, u.unstakeRewardDebt ); return args; } function _calculateRewardDebt( uint256 accRewardPerShare, uint256 power ) internal pure returns (uint256) { return (power * accRewardPerShare) / ACC_REWARD_PRECISION; } function _updateUser( PremiaStakingStorage.Layout storage l, PremiaStakingStorage.UserInfo storage u, UpdateArgsInternal memory args ) internal { // Update reward debt u.rewardDebt = _calculateRewardDebt(l.accRewardPerShare, args.newPower); u.unstakeRewardDebt = _calculateRewardDebt( l.accUnstakeRewardPerShare, args.newPower ); _creditRewards(l, u, args.user, args.reward, args.unstakeReward); _updateTotalPower(l, args.oldPower, args.newPower); } /** * @inheritdoc IPremiaStaking */ function getAvailablePremiaAmount() external view returns (uint256) { return _getAvailablePremiaAmount(); } function _getAvailablePremiaAmount() internal view returns (uint256) { return IERC20(PREMIA).balanceOf(address(this)) - PremiaStakingStorage.layout().pendingWithdrawal; } }
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; library PremiaStakingStorage { bytes32 internal constant STORAGE_SLOT = keccak256("premia.contracts.staking.PremiaStaking"); struct Withdrawal { uint256 amount; // Premia amount uint256 startDate; // Will unlock at startDate + withdrawalDelay } struct UserInfo { uint256 reward; // Amount of rewards accrued which havent been claimed yet uint256 rewardDebt; // Debt to subtract from reward calculation uint256 unstakeRewardDebt; // Debt to subtract from reward calculation from early unstake fee uint64 stakePeriod; // Stake period selected by user uint64 lockedUntil; // Timestamp at which the lock ends } struct Layout { uint256 pendingWithdrawal; uint256 _deprecated_withdrawalDelay; mapping(address => Withdrawal) withdrawals; uint256 availableRewards; uint256 lastRewardUpdate; // Timestamp of last reward distribution update uint256 totalPower; // Total power of all staked tokens (underlying amount with multiplier applied) mapping(address => UserInfo) userInfo; uint256 accRewardPerShare; uint256 accUnstakeRewardPerShare; uint256 availableUnstakeRewards; } function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
// SPDX-License-Identifier: BUSL-1.1 // For further clarification please see https://license.premia.legal pragma solidity ^0.8.0; library VxPremiaStorage { bytes32 internal constant STORAGE_SLOT = keccak256("premia.contracts.staking.VxPremia"); enum VoteVersion { V2 // poolAddress : 20 bytes / isCallPool : 2 bytes } struct Vote { uint256 amount; VoteVersion version; bytes target; } struct Layout { mapping(address => Vote[]) userVotes; // Vote version -> Pool identifier -> Vote amount mapping(VoteVersion => mapping(bytes => uint256)) votes; } function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"proxyManager","type":"address"},{"internalType":"address","name":"lzEndpoint","type":"address"},{"internalType":"address","name":"premia","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"address","name":"exchangeHelper","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressUtils__NotContract","type":"error"},{"inputs":[],"name":"BytesLib__OutOfBounds","type":"error"},{"inputs":[],"name":"BytesLib__Overflow","type":"error"},{"inputs":[],"name":"ECDSA__InvalidS","type":"error"},{"inputs":[],"name":"ECDSA__InvalidSignature","type":"error"},{"inputs":[],"name":"ECDSA__InvalidV","type":"error"},{"inputs":[],"name":"ERC165Base__InvalidInterfaceId","type":"error"},{"inputs":[],"name":"ERC20Base__ApproveFromZeroAddress","type":"error"},{"inputs":[],"name":"ERC20Base__ApproveToZeroAddress","type":"error"},{"inputs":[],"name":"ERC20Base__BurnExceedsBalance","type":"error"},{"inputs":[],"name":"ERC20Base__BurnFromZeroAddress","type":"error"},{"inputs":[],"name":"ERC20Base__InsufficientAllowance","type":"error"},{"inputs":[],"name":"ERC20Base__MintToZeroAddress","type":"error"},{"inputs":[],"name":"ERC20Base__TransferExceedsBalance","type":"error"},{"inputs":[],"name":"ERC20Base__TransferFromZeroAddress","type":"error"},{"inputs":[],"name":"ERC20Base__TransferToZeroAddress","type":"error"},{"inputs":[],"name":"ERC20Extended__ExcessiveAllowance","type":"error"},{"inputs":[],"name":"ERC20Extended__InsufficientAllowance","type":"error"},{"inputs":[],"name":"ERC20Permit__ExpiredDeadline","type":"error"},{"inputs":[],"name":"ERC20Permit__InvalidSignature","type":"error"},{"inputs":[],"name":"LzApp__InvalidEndpointCaller","type":"error"},{"inputs":[],"name":"LzApp__InvalidSource","type":"error"},{"inputs":[],"name":"LzApp__NoTrustedPathRecord","type":"error"},{"inputs":[],"name":"LzApp__NotTrustedSource","type":"error"},{"inputs":[],"name":"NonblockingLzApp__CallerNotLzApp","type":"error"},{"inputs":[],"name":"NonblockingLzApp__InvalidPayload","type":"error"},{"inputs":[],"name":"NonblockingLzApp__NoStoredMessage","type":"error"},{"inputs":[],"name":"OFT_InsufficientAllowance","type":"error"},{"inputs":[],"name":"Ownable__NotOwner","type":"error"},{"inputs":[],"name":"Ownable__NotTransitiveOwner","type":"error"},{"inputs":[],"name":"PremiaStaking__CantTransfer","type":"error"},{"inputs":[],"name":"PremiaStaking__ExcessiveStakePeriod","type":"error"},{"inputs":[],"name":"PremiaStaking__InsufficientSwapOutput","type":"error"},{"inputs":[],"name":"PremiaStaking__NoPendingWithdrawal","type":"error"},{"inputs":[],"name":"PremiaStaking__NotEnoughLiquidity","type":"error"},{"inputs":[],"name":"PremiaStaking__PeriodTooShort","type":"error"},{"inputs":[],"name":"PremiaStaking__StakeLocked","type":"error"},{"inputs":[],"name":"PremiaStaking__StakeNotLocked","type":"error"},{"inputs":[],"name":"PremiaStaking__WithdrawalStillPending","type":"error"},{"inputs":[],"name":"SafeERC20__OperationFailed","type":"error"},{"inputs":[],"name":"VxPremia__InvalidPoolAddress","type":"error"},{"inputs":[],"name":"VxPremia__InvalidVoteTarget","type":"error"},{"inputs":[],"name":"VxPremia__NotEnoughVotingPower","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"enum VxPremiaStorage.VoteVersion","name":"version","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"target","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AddVote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint64","name":"stakePeriod","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"lockedUntil","type":"uint64"}],"name":"BridgeLock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EarlyUnstakeRewardCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Harvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"srcChainId","type":"uint16"},{"indexed":false,"internalType":"bytes","name":"srcAddress","type":"bytes"},{"indexed":false,"internalType":"uint64","name":"nonce","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"payload","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"reason","type":"bytes"}],"name":"MessageFailed","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":"uint16","name":"srcChainId","type":"uint16"},{"indexed":true,"internalType":"bytes","name":"srcAddress","type":"bytes"},{"indexed":true,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReceiveFromChain","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"enum VxPremiaStorage.VoteVersion","name":"version","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"target","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RemoveVote","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"srcChainId","type":"uint16"},{"indexed":false,"internalType":"bytes","name":"srcAddress","type":"bytes"},{"indexed":false,"internalType":"uint64","name":"nonce","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"payloadHash","type":"bytes32"}],"name":"RetryMessageSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint16","name":"dstChainId","type":"uint16"},{"indexed":true,"internalType":"bytes","name":"toAddress","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SendToChain","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"_remoteChainId","type":"uint16"},{"indexed":false,"internalType":"bytes","name":"_remoteAddress","type":"bytes"}],"name":"SetTrustedRemoteAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_useCustomAdapterParams","type":"bool"}],"name":"SetUseCustomAdapterParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"stakePeriod","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"lockedUntil","type":"uint64"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startDate","type":"uint256"}],"name":"Unstake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint64","name":"oldStakePeriod","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newStakePeriod","type":"uint64"}],"name":"UpdateLock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"domainSeparator","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PT_SEND","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"enum VxPremiaStorage.VoteVersion","name":"version","type":"uint8"},{"internalType":"bytes","name":"target","type":"bytes"}],"internalType":"struct VxPremiaStorage.Vote[]","name":"votes","type":"tuple[]"}],"name":"castVotes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"circulatingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"earlyUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"dstChainId","type":"uint16"},{"internalType":"bytes","name":"toAddress","type":"bytes"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"useZro","type":"bool"},{"internalType":"bytes","name":"adapterParams","type":"bytes"}],"name":"estimateSendFee","outputs":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"zroFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes","name":"srcAddress","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"name":"failedMessages","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes","name":"srcAddress","type":"bytes"}],"name":"forceResumeReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAvailablePremiaAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAvailableRewards","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"},{"internalType":"uint256","name":"unstakeRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"version","type":"uint16"},{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"configType","type":"uint256"}],"name":"getConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getDiscountBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getEarlyUnstakeFeeBPS","outputs":[{"internalType":"uint256","name":"feePercentage","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getPendingUserRewards","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"unstakeReward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getPendingWithdrawal","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startDate","type":"uint256"},{"internalType":"uint256","name":"unlockDate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingWithdrawals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum VxPremiaStorage.VoteVersion","name":"version","type":"uint8"},{"internalType":"bytes","name":"target","type":"bytes"}],"name":"getPoolVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakeLevels","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"discountBPS","type":"uint256"}],"internalType":"struct IPremiaStaking.StakeLevel[]","name":"stakeLevels","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"period","type":"uint256"}],"name":"getStakePeriodMultiplierBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTotalPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_remoteChainId","type":"uint16"}],"name":"getTrustedRemoteAddress","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserInfo","outputs":[{"components":[{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"},{"internalType":"uint256","name":"unstakeRewardDebt","type":"uint256"},{"internalType":"uint64","name":"stakePeriod","type":"uint64"},{"internalType":"uint64","name":"lockedUntil","type":"uint64"}],"internalType":"struct PremiaStakingStorage.UserInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserVotes","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"enum VxPremiaStorage.VoteVersion","name":"version","type":"uint8"},{"internalType":"bytes","name":"target","type":"bytes"}],"internalType":"struct VxPremiaStorage.Vote[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"callee","type":"address"},{"internalType":"address","name":"allowanceTarget","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"address","name":"refundAddress","type":"address"}],"internalType":"struct IPremiaStaking.SwapArgs","name":"s","type":"tuple"},{"internalType":"uint64","name":"stakePeriod","type":"uint64"}],"name":"harvestAndStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes","name":"srcAddress","type":"bytes"}],"name":"isTrustedRemote","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lzEndpoint","outputs":[{"internalType":"contract ILayerZeroEndpoint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes","name":"srcAddress","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"lzReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes","name":"srcAddress","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"nonblockingLzReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"resetUserVotes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes","name":"srcAddress","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"retryMessage","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint16","name":"dstChainId","type":"uint16"},{"internalType":"bytes","name":"toAddress","type":"bytes"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"refundAddress","type":"address"},{"internalType":"address","name":"zroPaymentAddress","type":"address"},{"internalType":"bytes","name":"adapterParams","type":"bytes"}],"name":"sendFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"version","type":"uint16"},{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"uint256","name":"configType","type":"uint256"},{"internalType":"bytes","name":"config","type":"bytes"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"version","type":"uint16"}],"name":"setReceiveVersion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"version","type":"uint16"}],"name":"setSendVersion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"remoteChainId","type":"uint16"},{"internalType":"bytes","name":"remoteAddress","type":"bytes"}],"name":"setTrustedRemoteAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"period","type":"uint64"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"period","type":"uint64"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"stakeWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"startWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"period","type":"uint64"}],"name":"updateLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
610120346200014957601f620056d438819003918201601f19168301916001600160401b038311848410176200014e5780849260a09460405283398101031262000149576200004e8162000164565b906200005d6020820162000164565b906200006c6040820162000164565b620000886080620000806060850162000164565b930162000164565b6001600160a01b0390931660805260a05260c05260e05261010090815260405161555a91826200017a83396080518281816106900152818161095401528181610a7901528181610ccd01528181611204015281816122680152818161253701528181612b3e0152613775015260a05182818161109401528181611bcf01528181611e02015281816140fe0152614b40015260c05182818161128501528181611768015281816123070152614050015260e0518261407e0152518161516b0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001495756fe60806040526004361015610013575b600080fd5b60003560e01c80621d3567146104a557806301ffc9a71461049c57806302931e591461049357806306fdde031461048a57806307e0db171461048157806307fbf48a14610478578063095ea7b31461046f57806310ddb1371461046657806318160ddd1461036a57806323b872dd1461045d578063263aaee7146104545780632a205e3d1461044b5780632e6139f014610442578063313ce567146104395780633439c11a146104305780633644e51514610427578063395093511461041e5780633ccfd60b146104155780633d8b38f61461040c57806342d65a8d146104035780634641257d146103fa5780634c42899a146103f157806351905636146103e857806353976a26146103df5780635b8c41e6146103d65780636386c1c7146103cd57806366ad5c8a146103c457806369940d79146103bb57806370a08231146103b25780637c0712b4146103a95780637ecebe00146103a05780637ee8b2f814610397578063854c92311461038e57806385eff58e1461038557806386e9eac81461037c578063884a993a146103735780639358928b1461036a578063950c782214610361578063952e68cf1461035857806395d89b411461034f5780639f38369a14610346578063a457c2d71461033d578063a6c3d16514610334578063a9059cbb1461032b578063b353aaa714610322578063bc14538814610319578063beceed3914610310578063c37fc8a814610307578063c60d588d146102fe578063cbed8b9c146102f5578063d1deba1f146102ec578063d1e43684146102e3578063d505accf146102da578063d526763b146102d1578063d9621f9e146102c8578063dd62ed3e146102bf578063e7efed26146102b6578063f5ecbdbc146102ad5763fc9c99ac146102a557600080fd5b61000e612bb3565b5061000e612ad0565b5061000e6129eb565b5061000e61298d565b5061000e612971565b5061000e612949565b5061000e6127b9565b5061000e612689565b5061000e6125cf565b5061000e6124c7565b5061000e612394565b5061000e612368565b5061000e6122bf565b5061000e612297565b5061000e612251565b5061000e612224565b5061000e61208a565b5061000e612054565b5061000e611f32565b5061000e611e2f565b5061000e611dd6565b5061000e611d54565b5061000e610ad0565b5061000e611ba3565b5061000e611ad1565b5061000e611a71565b5061000e611a15565b5061000e6119a9565b5061000e611981565b5061000e6117bf565b5061000e611797565b5061000e611751565b5061000e611617565b5061000e611540565b5061000e6114a7565b5061000e61147b565b5061000e6112d1565b5061000e6112b4565b5061000e611264565b5061000e6111cb565b5061000e611119565b5061000e611004565b5061000e610f95565b5061000e610f32565b5061000e610f16565b5061000e610ed5565b5061000e610d5d565b5061000e610bd0565b5061000e610b5b565b5061000e610b0e565b5061000e610a34565b5061000e6109fd565b5061000e6109de565b5061000e61090f565b5061000e6108cb565b5061000e610816565b5061000e6107a3565b5061000e61067d565b61ffff81160361000e57565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b0381116104e457604052565b6104ec6104ba565b604052565b60a081019081106001600160401b038211176104e457604052565b606081019081106001600160401b038211176104e457604052565b604081019081106001600160401b038211176104e457604052565b60c081019081106001600160401b038211176104e457604052565b90601f801991011681019081106001600160401b038211176104e457604052565b6040519061058b82610527565b565b6020906001600160401b0381116105aa575b601f01601f19160190565b6105b26104ba565b61059f565b81601f8201121561000e578035906105ce8261058d565b926105dc604051948561055d565b8284526020838301011161000e57816000926020809301838601378301015290565b6001600160401b0381160361000e57565b6024359061058b826105fe565b90608060031983011261000e57600435610635816104ae565b916001600160401b039160243583811161000e5782610656916004016105b7565b92604435610663816105fe565b9260643591821161000e5761067a916004016105b7565b90565b503461000e5761068c3661061c565b92907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610791576106c883836133fe565b1561077f5761070a5a604051633356ae4560e11b602082015290610704826106f689878a8a6024860161345c565b03601f19810184528361055d565b306135d6565b901561071257005b8461077a927fe183f33de2837795525b4792ca4cd60535bd77c53b7e7030060bfcf5734d6b0c9651602083012061076d8261075561074f89613318565b8a613499565b906001600160401b0316600052602052604060002090565b55604051958695866134bf565b0390a1005b604051632d21758760e01b8152600490fd5b604051634961637b60e01b8152600490fd5b503461000e57602036600319011261000e5760043563ffffffff60e01b811680910361000e576000527ffc606c433378e3a7e0a6a531deac289b66caa1b4aa8554fd4ab2c6f1570f92d8602052602060ff604060002054166040519015158152f35b6001600160a01b0381160361000e57565b503461000e57602036600319011261000e57602061085f60043561083981610805565b6001600160401b03600361085661084f84612c10565b5493612c49565b01541690614831565b604051908152f35b600091031261000e57565b60005b8381106108855750506000910152565b8181015183820152602001610875565b906020916108ae81518092818552858086019101610872565b601f01601f1916010190565b90602061067a928181520190610895565b503461000e57600036600319011261000e5761090b6040516108f7816108f081612f5c565b038261055d565b604051918291602083526020830190610895565b0390f35b503461000e57600060203660031901126109db5760043561092f816104ae565b6000805160206154c5833981519152546001600160a01b0390811633036109c95782907f00000000000000000000000000000000000000000000000000000000000000001691823b156109c557602461ffff918360405195869485936307e0db1760e01b85521660048401525af180156109b8575b6109ac575080f35b6109b5906104d1565b80f35b6109c0613187565b6109a4565b5080fd5b604051632f7a8ee160e01b8152600490fd5b80fd5b503461000e57602036600319011261000e57602061085f6004356147ff565b503461000e57604036600319011261000e576020610a2a600435610a2081610805565b6024359033612d66565b6040519015158152f35b503461000e57600060203660031901126109db57600435610a54816104ae565b6000805160206154c5833981519152546001600160a01b0390811633036109c95782907f00000000000000000000000000000000000000000000000000000000000000001691823b156109c557602461ffff918360405195869485936310ddb13760e01b85521660048401525af180156109b8576109ac575080f35b503461000e57600036600319011261000e5760207fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b654604051908152f35b503461000e57606036600319011261000e57610b4f600435610b2f81610805565b602435610b3b81610805565b60443591610b4a833383612eec565b612e1e565b50602060405160018152f35b503461000e57600036600319011261000e57610b756146f7565b604080519182916020808401908085528351809252808386019401926000905b838210610ba25786860387f35b84518051875283015186840152879650948501949382019360019190910190610b95565b8015150361000e57565b503461000e5760a036600319011261000e57600435610bee816104ae565b6001600160401b0360243581811161000e57610c0e9036906004016105b7565b9060643591610c1c83610bc6565b6084359382851161000e57610cc96020610c3d610c9f9736906004016105b7565b93015192610cad6003610c4f86612c49565b015460408051600060208201526001600160a01b039097168782015260443560608801526001600160401b038883168116608089015291811c9097161660a08601529496879590859060c0820190565b03601f19810186528561055d565b845163040a7bb160e41b8152958694859430906004870161369d565b03817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa918215610d50575b6000918293610d1b575b50519081526020810191909152604090f35b81610d4192945061090b93503d8511610d49575b610d39818361055d565b810190613687565b929091610d09565b503d610d2f565b610d58613187565b610cff565b503461000e57602036600319011261000e57600435610d7b816105fe565b6001600160401b039081811691630784ce008311610ec357610d9b613f0c565b610da433612c49565b916003830180549083821680961115610eb1577f5cd2ddc163c3d93e18638ca23f2992d2a14b4ce98a66c629d85560b47c25f1c894610e6a848893610e4f610e2789610e1a610e0e610e889a610eac9d610dfe338c6148d2565b9b89031693429160401c16613fcd565b6001600160401b031690565b016001600160401b031690565b825467ffffffffffffffff60401b191660409190911b67ffffffffffffffff60401b16178255565b906001600160401b03166001600160401b0319825416179055565b610e7e84602084015160a085015101614831565b606083015261497d565b604080516001600160401b0395861681529490911660208501523393918291820190565b0390a2005b6040516305178a3b60e01b8152600490fd5b60405163897c6c7560e01b8152600490fd5b503461000e57600036600319011261000e57602060ff7f2967a798b92539a1b9eefe4d8eb931f96b68d27665e276f1bee2d5db7f7430495416604051908152f35b503461000e57600036600319011261000e57602061085f614b25565b503461000e576000806003193601126109db574681527fbb9c3660b51e1fafa886fcf600a68efa81371bd50359eccba98f0c4fff2cfeba60205260409020548015610f83575b602090604051908152f35b506020610f8e6130c6565b9050610f78565b503461000e57604036600319011261000e57600435610fb381610805565b610fd781610fc033612c82565b9060018060a01b0316600052602052604060002090565b54906024358201809211610ff257602091610a2a9133612d66565b60405163d256efb160e01b8152600490fd5b503461000e57600036600319011261000e5761101e613f0c565b600161102933612cbb565b0154801561110757620d2f00014211156110f55761104633612cbb565b54611075611063826000805160206154e5833981519152546133f1565b6000805160206154e583398151915255565b61108d61108133612cbb565b60016000918281550155565b6110c181337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166141a2565b60405190815233907f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364908060208101610eac565b60405163d7f0dd2d60e01b8152600490fd5b60405163b2ba9fb560e01b8152600490fd5b503461000e57604036600319011261000e57600435611137816104ae565b6024356001600160401b03811161000e5760209161115c610a2a9236906004016105b7565b906133fe565b9181601f8401121561000e578235916001600160401b03831161000e576020838186019501011161000e57565b90604060031983011261000e576004356111a8816104ae565b91602435906001600160401b03821161000e576111c791600401611162565b9091565b503461000e576111da3661118f565b6000805160206154c583398151915254600093926001600160a01b039291831633036109c95784927f00000000000000000000000000000000000000000000000000000000000000001690813b15611260578361124e95604051968795869485936342d65a8d60e01b8552600485016132fd565b03925af180156109b8576109ac575080f35b8380fd5b503461000e57600036600319011261000e576112b2611282336143f4565b337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166141a2565b005b503461000e57600036600319011261000e57602060405160008152f35b5060e036600319011261000e576004356112ea81610805565b602435906112f7826104ae565b6001600160401b0360443581811161000e576113179036906004016105b7565b50606435906084359061132982610805565b60a4359061133682610805565b60c4359181831161000e576114588561145d9381937f4a7c7bd7b3092a14241ec8138800e06869d4bf7ce2d955d6ada58e5ff3268a0a976114508b61138261ffff9a36906004016105b7565b9461138b613f0c565b6113958783614bc2565b6114446113a183612c49565b61142a6113ae85836148d2565b604051606087901b6bffffffffffffffffffffffff191660208201529c8d966113ee91906113e9896034810103601f199a8b8201815261055d565b61384a565b61140a6113ff8c60208401516133f1565b60a083015190612e11565b92610e7e600382019461142486546001600160401b031690565b90614831565b5460405198838a948360401c169216908c602086016136e1565b0390810186528561055d565b34938c61373d565b61371d565b6040519384529416926001600160a01b03169180602081015b0390a4005b503461000e57600036600319011261000e5760206000805160206154a583398151915254604051908152f35b503461000e57606036600319011261000e576004356114c5816104ae565b6024356001600160401b03811161000e57602091611516836114ee6115379436906004016105b7565b611503604435946114fe866105fe565b613318565b8260405194838680955193849201610872565b820190815203019020906001600160401b0316600052602052604060002090565b54604051908152f35b503461000e57602036600319011261000e5761090b60043561156181610805565b611591604091600060808451611576816104f1565b82815282602082015282868201528260608201520152612c49565b9060038151926115a0846104f1565b805484526001810154602085015260028101548385015201546001600160401b03908181166060850152821c1660808301525191829182919091608060a0820193805183526020810151602084015260408101516040840152816001600160401b0391826060820151166060860152015116910152565b503461000e576116263661061c565b91905030330361173f576020820161ffff80825116156000146116fa57835184019160a08584031261000e5761165c90516104ae565b60408401516001600160401b03811161000e576116a56116dc916020807f776434b505c7beb3db155c58df6c88985bf7c31730767e43ec773005059fed7a960191880101613275565b936114586060870151956116d460a060808a0151996116c38b6105fe565b0151916116cf836105fe565b613999565b9787896139be565b6040519384526001600160a01b039094169416918060208101611476565b60405162461bcd60e51b815260206004820152601c60248201527f4f4654436f72653a20756e6b6e6f776e207061636b65742074797065000000006044820152606490fd5b60405163f10781b160e01b8152600490fd5b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461000e57602036600319011261000e5760206115376004356117ba81610805565b612c10565b503461000e57602036600319011261000e576004356117dd33612c49565b906127106117f36117ed336144b2565b83613ed9565b04918282039182611802614b25565b1061196f577ffbd65cfd6de1493db337385c0712095397ecbd0504df64b861cdfceb80c7b422926118ab610eac93611838613f0c565b6118428433614bc2565b61184c33826148d2565b9061185785336138d0565b6000805160206154e5833981519152611871858254612e11565b9055876118f0575b610e7e61189861188d8760208601516133f1565b60a085015190612e11565b60038301546001600160401b0316611424565b6118bf6118b733612cbb565b918254612e11565b90554260016118cd33612cbb565b015560405191829133954291846040919493926060820195825260208201520152565b6119206118fc89613ea0565b61191a6000805160206154a5833981519152546040860151906133f1565b90613eec565b61193a600080516020615465833981519152918254612e11565b90557f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d66611968898254612e11565b9055611879565b60405163fde9a3e360e01b8152600490fd5b503461000e57602036600319011261000e5760206115376004356119a481610805565b612cf4565b503461000e57602036600319011261000e576004356119c781610805565b60009060016119df6119d883612cbb565b5492612cbb565b01549182611a09575b61090b90604051938493846040919493926060820195825260208201520152565b50620d2f0082016119e8565b503461000e57602036600319011261000e57600435611a3381610805565b6000805160206154c5833981519152546001600160a01b031633036109c95780611a5f6112b292612d2d565b61539c565b3590600182101561000e57565b503461000e57604036600319011261000e57600435600181101561000e57602435906001600160401b03821161000e576020611abf91611503611ab9839536906004016105b7565b91614dde565b82019081520301902054604051908152f35b503461000e5760031960403682011261000e57600435906001600160401b039081831161000e5760a090833603011261000e5760405190611b11826104f1565b826004013582526024830135611b2681610805565b60208301526044830135611b3981610805565b6040830152606483013590811161000e576112b292611b60608492600436918401016105b7565b60608401520135611b7081610805565b6080820152611b7d61060f565b9061403d565b6064359060ff8216820361000e57565b6084359060ff8216820361000e57565b503461000e5760c036600319011261000e57600435602435611bc4816105fe565b611bcc611b83565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b1561000e5781611c67916112b29560ff6040519163d505accf60e01b8352336004840152306024840152846044840152604435606484015216608482015260843560a482015260a43560c482015260008160e48183865af18015611c86575b611c6d575b5030903390613c21565b33614244565b80611c7a611c80926104d1565b80610867565b38611c5d565b611c8e613187565b611c58565b60011115611c9d57565b634e487b7160e01b600052602160045260246000fd5b60209081810182825283518091526040928383019281858460051b830101960194600080925b858410611ceb57505050505050505090565b9091929394959697603f19828203018852885180518252868101516001811015611d4057611d3083878a948594856001980152015190606090818a8201520190610895565b9a01980196959401929190611cd9565b634e487b7160e01b85526021600452602485fd5b503461000e5760208060031936011261000e57611d7b600435611d7681610805565b612d2d565b8054611d86816129c5565b91611d94604051938461055d565b8183526000908152838120938084015b838310611db9576040518061090b8782611cb3565b600382600192611dc889614ccd565b815201960192019194611da4565b503461000e57604036600319011261000e576112b2602435600435611dfa826105fe565b611c678130337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316613c21565b503461000e576000806003193601126109db5760405190807f2967a798b92539a1b9eefe4d8eb931f96b68d27665e276f1bee2d5db7f74304890815490611e7582612f22565b80865292600192808416908115611f055750600114611eab575b61090b86611e9f8188038261055d565b604051918291826108ba565b815292507f6626588b54b3522d930e3ee12195d0e9dddbde7c7325b02b73030918affce1c45b828410611eed575050508101602001611e9f8261090b38611e8f565b80546020858701810191909152909301928101611ed1565b905086955061090b96935060209250611e9f94915060ff191682840152151560051b820101929338611e8f565b503461000e5760208060031936011261000e57611f6e90611f5d600435611f58816104ae565b61334b565b611f75604091825194858092613031565b038461055d565b8251156120445782516013199384820190828211612037575b81611f9881612dfb565b1061202657818151106120155781611fc85750505061090b925080519160008352820181525b51918291826108ba565b839594955194601f8316801560051b91828289010195860101920101905b8084106120045750508352601f01601f1916815261090b9250611fbe565b815184529286019290860190611fe6565b835163aefe1d4360e01b8152600490fd5b8351632aaf81e960e01b8152600490fd5b61203f612de4565b611f8e565b5163ed2e153b60e01b8152600490fd5b503461000e57604036600319011261000e5761207f60043561207581610805565b6024359033612eec565b602060405160018152f35b503461000e576120993661118f565b6000805160206154c5833981519152549092906001600160a01b031633036109c9576040516020908484838301376120e66034828781013060601b8682015203601481018452018261055d565b6120ef8361334b565b918151916001600160401b038311612217575b612116836121108654612f22565b866133ac565b81601f84116001146121815750918061077a94927f8c0400cfe2d1199b1a725c78960bcc2a344d869b80590d0f2bd005db15a572ce9894600092612176575b50508160011b916000199060031b1c19161790555b604051938493846132fd565b015190503880612155565b9190601f19841661219786600052602060002090565b936000905b8282106121ff5750509260019285927f8c0400cfe2d1199b1a725c78960bcc2a344d869b80590d0f2bd005db15a572ce9a9661077a9896106121e6575b505050811b01905561216a565b015160001960f88460031b161c191690553880806121d9565b8060018697829497870151815501960194019061219c565b61221f6104ba565b612102565b503461000e57604036600319011261000e576020610a2a60043561224781610805565b6024359033612e1e565b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461000e57602036600319011261000e57602061085f6004356122ba81610805565b614587565b503461000e57602036600319011261000e577ff8fad42e780bfa5459be3fe691e8ba1aec70342250112139c5771c3fd155f31260206004356122ff613f0c565b6123348130337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316613c21565b60008051602061550583398151915280549082820180921161235b575b55604051908152a1005b612363612de4565b612351565b503461000e57600036600319011261000e5760206000805160206154e583398151915254604051908152f35b503461000e57602036600319011261000e576004356123b233612c49565b6003810180546001600160401b03429160401c16116124b557826123d4614b25565b1061196f57612450916123e5613f0c565b6123ef8433614bc2565b610e7e6123fc33836148d2565b9261240786336138d0565b6000805160206154e5833981519152612421878254612e11565b90556114246124426124378860208801516133f1565b60a087015190612e11565b91546001600160401b031690565b61245933612cbb565b612464828254612e11565b905542600161247233612cbb565b01556040805191825260006020830152429082015233907ffbd65cfd6de1493db337385c0712095397ecbd0504df64b861cdfceb80c7b422908060608101610eac565b6040516334e7720560e11b8152600490fd5b503461000e57608036600319011261000e576004356124e5816104ae565b6024356124f1816104ae565b6064356001600160401b03811161000e57612510903690600401611162565b6000805160206154c583398151915254919390916001600160a01b0390811633036109c9577f00000000000000000000000000000000000000000000000000000000000000001690813b1561000e57600080946125a4604051978896879586946332fb62e760e21b865261ffff809216600487015216602485015260443560448501526080606485015260848401916132dc565b03925af180156125c2575b6125b557005b80611c7a6112b2926104d1565b6125ca613187565b6125af565b506125d93661061c565b6125fe826125e8869496613318565b6020604051809288516115168184868d01610872565b54801561267857808251602084012003612667578461265b61077a9360006126537fc264d91f3adc5588250e1551f547752ca0cfa8f6b530d243b9f9f4cab10ea8e59961075561264d8a613318565b8b613499565b558686613544565b6040519485948561350e565b60405162451eff60e81b8152600490fd5b604051629c96ed60e61b8152600490fd5b503461000e57602036600319011261000e576004356126a781610805565b6126b081612c49565b60008051602061548583398151915254907f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d61541515806127a1575b61275e575b61273061271761270361274c9495612c10565b5460038401546001600160401b0316611424565b9361272a83549186600186015491614854565b90612e11565b9260026000805160206154658339815191525492015491614854565b60408051928352602083019190915290f35b61273061271761270361279761274c9561272a61278161277c613d82565b613ea0565b6000805160206154a58339815191525490613eec565b94505050506126f0565b506000805160206155058339815191525415156126eb565b503461000e5760e036600319011261000e576004356127d781610805565b6024356127e381610805565b604435906064356127f2611b93565b8142116129375760c06128b09261280887612cf4565b5490604051917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98352886020840152866040840152876060840152608083015260a082015220612881466000527fbb9c3660b51e1fafa886fcf600a68efa81371bd50359eccba98f0c4fff2cfeba602052604060002090565b549081156128f2575b6040519161190160f01b83526002830152602282015260c43591604260a4359220613194565b6001600160a01b038481169116036128e057826128cf6112b294612cf4565b6128d9815461316b565b9055612d66565b604051637570072b60e11b8152600490fd5b90506128fc6130c6565b9081612931466000527fbb9c3660b51e1fafa886fcf600a68efa81371bd50359eccba98f0c4fff2cfeba602052604060002090565b5561288a565b60405163384f7ce560e21b8152600490fd5b503461000e57602036600319011261000e57602061085f60043561296c81610805565b6144b2565b503461000e57600036600319011261000e57602061085f613d82565b503461000e57604036600319011261000e5760206115376004356129b081610805565b610fc0602435916129c083610805565b612c82565b6020906001600160401b0381116129de575b60051b0190565b6129e66104ba565b6129d7565b503461000e5760208060031936011261000e576001600160401b0360043581811161000e573660238201121561000e578060040135612a29816129c5565b92604090612a398251958661055d565b828552858501916024809460051b8601019436861161000e57848101935b868510612a67576112b288615117565b843584811161000e5782016060602319823603011261000e57835191612a8c8361050c565b878201358352612a9e60448301611a64565b8b84015260648201359286841161000e57612ac18c94938a8695369201016105b7565b86820152815201940193612a57565b503461000e57608036600319011261000e5761090b600435612af1816104ae565b60243590612afe826104ae565b612b09604435610805565b604051633d7b2f6f60e21b815261ffff91821660048201529116602482015230604482015260648035908201526000816084817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115612ba6575b600091612b85575b50604051918291826108ba565b612ba0913d8091833e612b98818361055d565b8101906132b7565b38612b78565b612bae613187565b612b70565b503461000e57600036600319011261000e5760008051602061550583398151915254612bdd613d82565b7f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d6654604080519290930382526020820152f35b6001600160a01b031660009081527fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b46020526040902090565b6001600160a01b031660009081527f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d636020526040902090565b6001600160a01b031660009081527fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b56020526040902090565b6001600160a01b031660009081527f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d5f6020526040902090565b6001600160a01b031660009081527fbb9c3660b51e1fafa886fcf600a68efa81371bd50359eccba98f0c4fff2cfeb96020526040902090565b6001600160a01b031660009081527fedf5b05b5f3c4999fbcd4c981b71eaaa791221cf2deabf009ac3784dd586d35a6020526040902090565b6001600160a01b038082169291908315612dd2578216938415612dc15780612db47f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92594610fc0602095612c82565b55604051908152a3600190565b6040516262920b60e91b8152600490fd5b604051635a68b7ab60e01b8152600490fd5b50634e487b7160e01b600052601160045260246000fd5b90601f8201809211612e0957565b61058b612de4565b91908201809211612e0957565b6001600160a01b038082169291908315612eda578216938415612ec857612e458383613650565b612e4e82612c10565b54808211612eb6577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef93612eae93612e8b84612e91940391612c10565b55612c10565b612e9c828254612e11565b90556040519081529081906020820190565b0390a3600190565b604051637183160b60e01b8152600490fd5b6040516320a2645160e21b8152600490fd5b6040516368551d5f60e01b8152600490fd5b90612efa81610fc084612c82565b5492838111612f1157612f0e930391612d66565b50565b60405162e14ccf60e11b8152600490fd5b90600182811c92168015612f52575b6020831014612f3c57565b634e487b7160e01b600052602260045260246000fd5b91607f1691612f31565b906000917f2967a798b92539a1b9eefe4d8eb931f96b68d27665e276f1bee2d5db7f743047908154612f8d81612f22565b8083529260019180831690811561300c5750600114612fad575b50505050565b90929394506000527fc3d5862a69f8ca70e4828043821a8d13dfd34088ddb52ba52f4c8aedc2ea63b5916000925b848410612ff45750506020925001019038808080612fa7565b80546020858501810191909152909301928101612fdb565b92505050602093945060ff929192191683830152151560051b01019038808080612fa7565b906000929180549161304283612f22565b9182825260019384811690816000146130a357506001146130635750505050565b90919394506000526020928360002092846000945b83861061308f575050505001019038808080612fa7565b805485870183015294019385908201613078565b9294505050602093945060ff191683830152151560051b01019038808080612fa7565b6040516130d6816108f081612f5c565b60208151910120603160f81b60206040516130f081610527565b60018152015260405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815261316581610542565b51902090565b600190600019811461317b570190565b613183612de4565b0190565b506040513d6000823e3d90fd5b9291927f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116132635760ff8216601b8114159081613257575b5061324557613202600093602095604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa15613238575b6000516001600160a01b038116156132265790565b6040516347f3b32760e11b8152600490fd5b613240613187565b613211565b604051630acd54a960e41b8152600490fd5b601c91501415386131ce565b60405163e4a61add60e01b8152600490fd5b81601f8201121561000e57805161328b8161058d565b92613299604051948561055d565b8184526020828401011161000e5761067a9160208085019101610872565b9060208282031261000e5781516001600160401b03811161000e5761067a9201613275565b908060209392818452848401376000828201840152601f01601f1916010190565b60409061ffff61067a959316815281602082015201916132dc565b61ffff166000527f090abe8f4a9a553a6a365135ae432dc8ce193b30c76d2c3ec06109c134e9b111602052604060002090565b61ffff166000527f32b0ffe8e22ea97a097b9fe1753a56cad70a64a02675ba49c9f49d75c2b80bb8602052604060002090565b50634e487b7160e01b600052600060045260246000fd5b8181106133a0575050565b60008155600101613395565b9190601f81116133bb57505050565b61058b926000526020600020906020601f840160051c830193106133e7575b601f0160051c0190613395565b90915081906133da565b91908203918211612e0957565b9061342161340e61341a9361334b565b60405193848092613031565b038361055d565b805182518082149384613452575b8461343c575b5050505090565b6020929394508201209201201438808080613435565b811515945061342f565b9061348561067a959361ffff6001600160401b0393168452608060208501526080840190610895565b931660408201526060818403910152610895565b6020906134b3928260405194838680955193849201610872565b82019081520301902090565b936001600160401b036134ec6135009461ffff61067a99979516885260a0602089015260a0880190610895565b921660408601528482036060860152610895565b916080818403910152610895565b9061353960609361ffff6001600160401b039398979698168452608060208501526080840190610895565b951660408201520152565b91906020820161ffff80825116156000146116fa57835184019160a08584031261000e5761357290516104ae565b60408401516001600160401b03811161000e576116a56135bb916020807f776434b505c7beb3db155c58df6c88985bf7c31730767e43ec773005059fed7a960191880101613275565b6040519384526001600160a01b0394909416941691602090a4565b90929160008091604051956135ea87610542565b6096875282602088019560a036883760208451940192f1903d9060968211613618575b6000908286523e9190565b6096915061360d565b6001600160a01b0316158015613648575b61058b576040516345de49e960e01b8152600490fd5b506001613632565b6001600160a01b039081161591821561367c575b505061058b576040516345de49e960e01b8152600490fd5b161590503880613664565b919082604091031261000e576020825192015190565b919261067a959361ffff6136cc9316845260018060a01b0316602084015260a0604084015260a0830190610895565b92151560608201526080818403910152610895565b92909361370160809396956000865260a0602087015260a0860190610895565b9560408501526001600160401b03809216606085015216910152565b61373590602060405192828480945193849201610872565b810103902090565b9461375d939194956137646137518261334b565b60405196878092613031565b038661055d565b845115613838576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811694909390853b1561000e576137de986000988661380c9461ffff986137f16040519e8f9d8e9c8d9b62c5803160e81b8d521660048c015260c060248c015260c48b0190610895565b60031995868b83030160448c0152610895565b951660648801521660848601528483030160a4850152610895565b03925af1801561382b575b61381e5750565b80611c7a61058b926104d1565b613833613187565b613817565b604051631afd825f60e31b8152600490fd5b90336001600160a01b03831603613865575b61058b916138d0565b61386e33612c82565b3360009081526020829052604090205492908383116138be576138ae6138b6918461058b96039182913360018060a01b0316600052602052604060002090565b553383612d66565b50915061385c565b60405163b09ec56160e01b8152600490fd5b6001600160a01b038116908115613987576138ea81613621565b6138f381612c10565b5480841161397557837fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9261392e6000966020940391612c10565b557fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b6805490828203918211613968575b55604051908152a3565b613970612de4565b61395e565b60405163b1d35b2360e01b8152600490fd5b6040516378f1627760e11b8152600490fd5b60148151106139ac576020015160601c90565b60405163aefe1d4360e01b8152600490fd5b91906139c8613f0c565b6139d183612c49565b906139dc84836148d2565b906003830193816139f886546001600160401b039060401c1690565b916001600160401b0380809a166000428211600014613b535750429003935b16600042821115613b4b5750429003905b60a086019384518401926020880193845191613a43936141fe565b6001600160401b0316428b16016001600160401b0316885467ffffffffffffffff60401b1916604082901b67ffffffffffffffff60401b161789559988546001600160401b03169085518501908085519316931692613aa1936141fe565b6001600160401b0316875467ffffffffffffffff19166001600160401b0390911617875551019051018454613adc906001600160401b031690565b613ae591614831565b6060830152613af49085613b5b565b613afd9161497d565b54604080516001600160401b0392831681529390911660208401526001600160a01b0391909116917f89bf27326cff6f41c8f98732f3100cce6cd16f1ae76bb214139d60e283809f449190a2565b905090613a28565b905093613a17565b6001600160a01b0316908115613c0f577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000927fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b6805490828201809211613c02575b558484527fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b48252604084208054908282018092116139685755604051908152a3565b613c0a612de4565b613bc0565b60405163da007acd60e01b8152600490fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815261058b91613c63826104f1565b613cac9160405191613c7483610527565b60208084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908401526001600160a01b0316613ce9565b805180613cb7575050565b816020918101031261000e5760200151613cd081610bc6565b15613cd757565b6040516306dacbc160e51b8152600490fd5b9190606092803b15613d705781600092918360208194519301915af1913d15613d6a57503d91613d188361058d565b92613d26604051948561055d565b83523d6000602085013e5b15613d3a575090565b815115613d4a5750805190602001fd5b60405162461bcd60e51b8152908190613d6690600483016108ba565b0390fd5b91613d31565b604051632270d6bf60e21b8152600490fd5b600080516020615505833981519152547f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d61544203428111613e93575b600160801b906f7ffffdbc2dee24e1000000000000000090805b613e2457505090613e0d8161067a9360401c60016001607f1b031981121580613e13575b613e059061469a565b600f0b6146a1565b906133f1565b5060016001607f1b03811315613dfc565b60018116613e87575b607f918002821c60028216613e7c575b8002821c91600492838316613e71575b8002811c60088316613e66575b8002901c911c80613dd8565b809402811c93613e5a565b809402811c93613e4d565b809302821c92613e3d565b918102607f1c91613e2d565b613e9b612de4565b613dbe565b906c0c9f2c9cd04674edea4000000091828102928184041490151715612e0957565b90610bb891828102928184041490151715612e0957565b81810292918115918404141715612e0957565b8115613ef6570490565b634e487b7160e01b600052601260045260246000fd5b7f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d618054158015613fb6575b8015613f9f575b613f9a57613f4a613d82565b613f5661278182613ea0565b6000805160206154858339815191528054918201809211613f8d575b55600080516020615505833981519152908154039055429055565b613f95612de4565b613f72565b429055565b506000805160206155058339815191525415613f3e565b506000805160206154a58339815191525415613f37565b9080821115613fda575090565b905090565b9081602091031261000e575190565b9591909460c095614036949599989960018060a01b0396878095818095168c521660208b015260408a015216606088015216608086015260e060a086015260e0850190610895565b9416910152565b90614047336143f4565b91821561419d577f0000000000000000000000000000000000000000000000000000000000000000926020906001600160a01b03907f0000000000000000000000000000000000000000000000000000000000000000906140ab8183858a166141a2565b848401516000906001600160a01b031660408701519094906001600160a01b031660608801516080890151919a9161412791906001600160a01b0316906040519c8d998a988996632e25a7ab60e01b88527f00000000000000000000000000000000000000000000000000000000000000009060048901613fee565b0393165af1928315614190575b600093614160575b5051821061414e5761058b9133614244565b604051630c96bfff60e41b8152600490fd5b61418291935060203d8111614189575b61417a818361055d565b810190613fdf565b913861413c565b503d614170565b614198613187565b614134565b505050565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815261058b91608082016001600160401b038111838210176141f1575b604052613c63565b6141f96104ba565b6141e9565b92614216816142108561067a97613ed9565b93613ed9565b8201809211614237575b820180921115613eec57614232612de4565b613eec565b61423f612de4565b614220565b9190916001600160401b039182811690630784ce008211610ec357838042169182011690614270613f0c565b61427984612c49565b918661428586856148d2565b926003850195886142a188546001600160401b039060401c1690565b946000428211156143ec5750429003945b166000428211156143e45750429003915b60a0860194855185019360208801948551916142de936141fe565b6001600160401b0316016001600160401b0316875467ffffffffffffffff60401b1916604082901b67ffffffffffffffff60401b161788559887546001600160401b031690855185019084519216614335936141fe565b6001600160401b0316865467ffffffffffffffff19166001600160401b0390911617865551019051018354614370906001600160401b031690565b61437991614831565b60608201526143888685613b5b565b6143919161497d565b54604080519485526001600160401b0391821660208601529216918301919091526001600160a01b0316907fd9033c965af127d7b3cd5e17d24e62287aec96a7a01a26a1aded18f1fa4765d090606090a2565b9050916142c3565b9050946142b2565b906143fd613f0c565b61440682612c49565b9161445361441482856148d2565b60a081018051156144a25761444790602083015190518101809111614495575b60038701546001600160401b0316611424565b60608201525b8461497d565b825460009093556040518381526001600160a01b0391909116907fc9695243a805adb74c91f28311176c65b417e842d5699893cef56d18bfa48cba90602090a2565b61449d612de4565b614434565b506040810151606082015261444d565b60036144c56001600160401b0392612c49565b015460401c16428111156144f3576109c46301e1338091429003020490611d4c8083116144ef5750565b9150565b6040516399067dc360e01b8152600490fd5b50634e487b7160e01b600052603260045260246000fd5b60209080511561452a570190565b613183614505565b60409080516001101561452a570190565b60609080516002101561452a570190565b60809080516003101561452a570190565b602091815181101561457a575b60051b010190565b614582614505565b614572565b6145b061459382612c10565b5461142460036145a285612c49565b01546001600160401b031690565b903b61466c576145be6146f7565b9081519060005b8281106145e557506020926145df92600019019150614565565b51015190565b6145ef8185614565565b518051831061460157506001016145c5565b919392506000811561465657506131839293612710602061463561464d946000190161462d818a614565565b515198614565565b510151955b8087602087015103955103920302613eec565b02612710900490565b613183935061464d91509361271060009561463a565b6000805160206154a58339815191525460011c80821061468e575050610bb890565b61423261067a92613ec2565b1561000e57565b81156146f057600f0b6000908181126109c5576fffffffffffffffffffffffffffffffff8316810260401c9260801c026001600160c01b0381116109c55760401b90821982116109db57500190565b5050600090565b604090815191614706836104f1565b600483526000805b608081106147dd57505050612f0e8261472561057e565b69010f0cf064dd5920000081526103e860208201526147438261451c565b5261474d8161451c565b5061475661057e565b690a968163f0a57b40000081526109c4602082015261477482614532565b5261477e81614532565b5061478761057e565b6969e10de76676d08000008152610dac60208201526147a582614543565b526147af81614543565b506147b861057e565b6a0211654585005212800000815261177060208201526147d782614554565b52614554565b60209083516147eb81610527565b83815282848183015282880101520161470e565b801561482a57630784ce00811015614823576127106301e133809102046109c40190565b5061a60490565b506109c490565b6148509061484a6001600160401b0361271094166147ff565b90613ed9565b0490565b6c0c9f2c9cd04674edea400000009161486c91613ed9565b0490810390811161487a5790565b61067a612de4565b6040519060c082018281106001600160401b038211176148c5575b6040528160a06000918281528260208201528260408201528260608201528260808201520152565b6148cd6104ba565b61489d565b614952906148de614882565b506148fc6148ea614882565b6001600160a01b038516815293612c10565b548060208501528061495a575b50600080516020615485833981519152549061493060408501928351600184015491614854565b6080850152600260008051602061546583398151915254925191015491614854565b60a082015290565b614972906001600160401b0360038401541690614831565b604084015238614909565b604061058b9260008051602061548583398151915254926149a46060830194855190614b0d565b60018201556149c460008051602061546583398151915254855190614b0d565b600282015560018060a01b0382511690608083015160a0840151918054918201809211614a77575b55806149ff575b50500151905190614a84565b60207f7fe22991e338d21d6aa37b281a268bca0c6a3c44451c34cc7dc9f42cb6ab9ecc917f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d66805490828203918211614a6a575b55614a5d8185613b5b565b8551908152a238806149f3565b614a72612de4565b614a52565b614a7f612de4565b6149ec565b80821115614acc578103908111614abf575b6000805160206154a58339815191528054918201809211614ab45755565b614abc612de4565b55565b614ac7612de4565b614a96565b90818110614ad8575050565b8103908111614b00575b6000805160206154a58339815191528054918203918211614ab45755565b614b08612de4565b614ae2565b614850906c0c9f2c9cd04674edea4000000092613ed9565b6040516370a0823160e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115614bb5575b600091614b97575b506000805160206154e583398151915254810390811161487a5790565b614baf915060203d81116141895761417a818361055d565b38614b7a565b614bbd613187565b614b72565b90916001600160401b0390614bf7614be1600395848761085688612c49565b92614beb85612c10565b54908661085687612c49565b92600092614c0482612d2d565b95865490614c11826129c5565b97614c1f604051998a61055d565b8289526020808a0191885280882088925b858410614c8757505050505050835b8651851015614c6057600190614c558689614565565b515101940193614c3f565b9490919350614c709295506133f1565b808311614c7c57505050565b61058b920390614e32565b8483600192614c9585614ccd565b815201920193019290614c30565b8054821015614cc0575b6000526003602060002091020190600090565b614cc8614505565b614cad565b90604051606081018181106001600160401b03821117614d24575b60405280928054825260ff600182015416916001831015611c9d57614d206040926002946020840152611f6e84518096819301613031565b0152565b614d2c6104ba565b614ce8565b80548015614dc85760001901906002614d4a8383614ca3565b614dbb575b600081556000600182015501614d658154612f22565b9081614d7057505055565b601f8211600114614d8357600091505555565b614dab614abc926001601f614d9d85600052602060002090565b920160051c82019101613395565b6000908082528160208120915555565b614dc361337e565b614d4f565b634e487b7160e01b600052603160045260246000fd5b6001811015611c9d576000527fedf5b05b5f3c4999fbcd4c981b71eaaa791221cf2deabf009ac3784dd586d35b602052604060002090565b929190614e2d602091604086526040860190610895565b930152565b614e3b81612d2d565b918254805b614e4a5750505050565b60001901614e61614e5b8286614ca3565b50614ccd565b918251808210600014614f1a57508080614e7b8488614ca3565b508181540390555b03927f0690a72b405552454fdede08f841412e53cde7110c70cbe1e8676c84ef6d0ff9614f066020830193614ec18551614ebc81611c93565b614dde565b94614ed26040809601968751613499565b8281540390555194614ee386611c93565b5193614eee86611c93565b516001600160a01b0389169490928392909183614e16565b0390a38115614f155780614e40565b612fa7565b8091614f2587614d31565b614e83565b602090818184031261000e578051906001600160401b03821161000e57019180601f8401121561000e578251614f5f816129c5565b93614f6d604051958661055d565b818552838086019260051b82010192831161000e578301905b828210614f94575050505090565b8380918351614fa281610805565b815201910190614f86565b602081519101519060208110614fc1575090565b6000199060200360031b1b1690565b614ff481546801000000000000000081101561510a575b6001928382018155614ca3565b6150fd575b82518155818101602091828501519084821015611c9d5760409260029260ff8019835416911617905501930151908151916001600160401b0383116150f0575b61504d836150478754612f22565b876133ac565b81601f8411600114615086575092829391839260009461507b575b50501b916000199060031b1c1916179055565b015192503880615068565b919083601f19811661509d88600052602060002090565b946000905b888383106150d657505050106150bd575b505050811b019055565b015160001960f88460031b161c191690553880806150b3565b8587015188559096019594850194879350908101906150a2565b6150f86104ba565b615039565b61510561337e565b614ff9565b6151126104ba565b614fe7565b9061513361512433612c10565b5461142460036145a233612c49565b9061513d33612d2d565b615147338261539c565b604093845191636a0ee5f560e11b8352600090600460018060a01b0396838683818b7f0000000000000000000000000000000000000000000000000000000000000000165afa95861561538f575b849661536b575b50959294938598979894865b825188101561535e576151c86151be8985614565565b5197885190612e11565b9a898c1161534e57602088019687516151e081611c93565b6151e981611c93565b1580159061533f575b61532f578b9c61522661521a61521a6152149d9e9f8d999799019d8e51614fad565b60601c90565b6001600160a01b031690565b95809d84829816975b875181101561531d5761525561521a615248838b614565565b516001600160a01b031690565b8914615269576152649061316b565b61522f565b509650989099919b93949a929c5060015b1561530d57907f0fb3ba678f396dab592d016f21ea8ea2e919c6463c0de53fe8a8613bd53222296152f88f6153009695946152b5858d614fd0565b84516152d26118b76152cb8451614ebc81611c93565b8951613499565b905551946152df86611c93565b519351906152ec86611c93565b51918291339583614e16565b0390a361316b565b96909197949998996151a8565b8d516321cdded960e21b81528790fd5b509650989099919b93949a929c61527a565b8b5163350e118b60e21b81528690fd5b5060158c8a01515114156151f2565b8a5163016a795f60e71b81528590fd5b5050505050945050505050565b6153889196503d8086833e615380818361055d565b810190614f2a565b943861519c565b615397613187565b615195565b908154805b6153aa57505050565b600019016153bb614e5b8285614ca3565b8051907f0690a72b405552454fdede08f841412e53cde7110c70cbe1e8676c84ef6d0ff961544560208301936153f58551614ebc81611c93565b94604091615407838701978851613499565b8054918203918211615457575b55519461542086611c93565b5193519061542d86611c93565b516001600160a01b0388169490928392909183614e16565b0390a38061545284614d31565b6153a1565b61545f612de4565b61541456fe93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d6593fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d6493fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d628a22373512790c48b83a1fe2efdd2888d4a917bcdc24d0adf63e60f67168046093fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d5d93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d60a2646970667358221220023a2bdf111b01ce6c0419344d77c23bbdcf10fc37e7894cde7947eda28ac2f064736f6c634300081100330000000000000000000000004f273f4efa9ecf5dd245a338fad9fe0bab63b35000000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6750000000000000000000000006399c842dd2be3de30bf99bc7d1bbf6fa3650e70000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb5
Deployed Bytecode
0x60806040526004361015610013575b600080fd5b60003560e01c80621d3567146104a557806301ffc9a71461049c57806302931e591461049357806306fdde031461048a57806307e0db171461048157806307fbf48a14610478578063095ea7b31461046f57806310ddb1371461046657806318160ddd1461036a57806323b872dd1461045d578063263aaee7146104545780632a205e3d1461044b5780632e6139f014610442578063313ce567146104395780633439c11a146104305780633644e51514610427578063395093511461041e5780633ccfd60b146104155780633d8b38f61461040c57806342d65a8d146104035780634641257d146103fa5780634c42899a146103f157806351905636146103e857806353976a26146103df5780635b8c41e6146103d65780636386c1c7146103cd57806366ad5c8a146103c457806369940d79146103bb57806370a08231146103b25780637c0712b4146103a95780637ecebe00146103a05780637ee8b2f814610397578063854c92311461038e57806385eff58e1461038557806386e9eac81461037c578063884a993a146103735780639358928b1461036a578063950c782214610361578063952e68cf1461035857806395d89b411461034f5780639f38369a14610346578063a457c2d71461033d578063a6c3d16514610334578063a9059cbb1461032b578063b353aaa714610322578063bc14538814610319578063beceed3914610310578063c37fc8a814610307578063c60d588d146102fe578063cbed8b9c146102f5578063d1deba1f146102ec578063d1e43684146102e3578063d505accf146102da578063d526763b146102d1578063d9621f9e146102c8578063dd62ed3e146102bf578063e7efed26146102b6578063f5ecbdbc146102ad5763fc9c99ac146102a557600080fd5b61000e612bb3565b5061000e612ad0565b5061000e6129eb565b5061000e61298d565b5061000e612971565b5061000e612949565b5061000e6127b9565b5061000e612689565b5061000e6125cf565b5061000e6124c7565b5061000e612394565b5061000e612368565b5061000e6122bf565b5061000e612297565b5061000e612251565b5061000e612224565b5061000e61208a565b5061000e612054565b5061000e611f32565b5061000e611e2f565b5061000e611dd6565b5061000e611d54565b5061000e610ad0565b5061000e611ba3565b5061000e611ad1565b5061000e611a71565b5061000e611a15565b5061000e6119a9565b5061000e611981565b5061000e6117bf565b5061000e611797565b5061000e611751565b5061000e611617565b5061000e611540565b5061000e6114a7565b5061000e61147b565b5061000e6112d1565b5061000e6112b4565b5061000e611264565b5061000e6111cb565b5061000e611119565b5061000e611004565b5061000e610f95565b5061000e610f32565b5061000e610f16565b5061000e610ed5565b5061000e610d5d565b5061000e610bd0565b5061000e610b5b565b5061000e610b0e565b5061000e610a34565b5061000e6109fd565b5061000e6109de565b5061000e61090f565b5061000e6108cb565b5061000e610816565b5061000e6107a3565b5061000e61067d565b61ffff81160361000e57565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b0381116104e457604052565b6104ec6104ba565b604052565b60a081019081106001600160401b038211176104e457604052565b606081019081106001600160401b038211176104e457604052565b604081019081106001600160401b038211176104e457604052565b60c081019081106001600160401b038211176104e457604052565b90601f801991011681019081106001600160401b038211176104e457604052565b6040519061058b82610527565b565b6020906001600160401b0381116105aa575b601f01601f19160190565b6105b26104ba565b61059f565b81601f8201121561000e578035906105ce8261058d565b926105dc604051948561055d565b8284526020838301011161000e57816000926020809301838601378301015290565b6001600160401b0381160361000e57565b6024359061058b826105fe565b90608060031983011261000e57600435610635816104ae565b916001600160401b039160243583811161000e5782610656916004016105b7565b92604435610663816105fe565b9260643591821161000e5761067a916004016105b7565b90565b503461000e5761068c3661061c565b92907f00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6756001600160a01b03163303610791576106c883836133fe565b1561077f5761070a5a604051633356ae4560e11b602082015290610704826106f689878a8a6024860161345c565b03601f19810184528361055d565b306135d6565b901561071257005b8461077a927fe183f33de2837795525b4792ca4cd60535bd77c53b7e7030060bfcf5734d6b0c9651602083012061076d8261075561074f89613318565b8a613499565b906001600160401b0316600052602052604060002090565b55604051958695866134bf565b0390a1005b604051632d21758760e01b8152600490fd5b604051634961637b60e01b8152600490fd5b503461000e57602036600319011261000e5760043563ffffffff60e01b811680910361000e576000527ffc606c433378e3a7e0a6a531deac289b66caa1b4aa8554fd4ab2c6f1570f92d8602052602060ff604060002054166040519015158152f35b6001600160a01b0381160361000e57565b503461000e57602036600319011261000e57602061085f60043561083981610805565b6001600160401b03600361085661084f84612c10565b5493612c49565b01541690614831565b604051908152f35b600091031261000e57565b60005b8381106108855750506000910152565b8181015183820152602001610875565b906020916108ae81518092818552858086019101610872565b601f01601f1916010190565b90602061067a928181520190610895565b503461000e57600036600319011261000e5761090b6040516108f7816108f081612f5c565b038261055d565b604051918291602083526020830190610895565b0390f35b503461000e57600060203660031901126109db5760043561092f816104ae565b6000805160206154c5833981519152546001600160a01b0390811633036109c95782907f00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6751691823b156109c557602461ffff918360405195869485936307e0db1760e01b85521660048401525af180156109b8575b6109ac575080f35b6109b5906104d1565b80f35b6109c0613187565b6109a4565b5080fd5b604051632f7a8ee160e01b8152600490fd5b80fd5b503461000e57602036600319011261000e57602061085f6004356147ff565b503461000e57604036600319011261000e576020610a2a600435610a2081610805565b6024359033612d66565b6040519015158152f35b503461000e57600060203660031901126109db57600435610a54816104ae565b6000805160206154c5833981519152546001600160a01b0390811633036109c95782907f00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6751691823b156109c557602461ffff918360405195869485936310ddb13760e01b85521660048401525af180156109b8576109ac575080f35b503461000e57600036600319011261000e5760207fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b654604051908152f35b503461000e57606036600319011261000e57610b4f600435610b2f81610805565b602435610b3b81610805565b60443591610b4a833383612eec565b612e1e565b50602060405160018152f35b503461000e57600036600319011261000e57610b756146f7565b604080519182916020808401908085528351809252808386019401926000905b838210610ba25786860387f35b84518051875283015186840152879650948501949382019360019190910190610b95565b8015150361000e57565b503461000e5760a036600319011261000e57600435610bee816104ae565b6001600160401b0360243581811161000e57610c0e9036906004016105b7565b9060643591610c1c83610bc6565b6084359382851161000e57610cc96020610c3d610c9f9736906004016105b7565b93015192610cad6003610c4f86612c49565b015460408051600060208201526001600160a01b039097168782015260443560608801526001600160401b038883168116608089015291811c9097161660a08601529496879590859060c0820190565b03601f19810186528561055d565b845163040a7bb160e41b8152958694859430906004870161369d565b03817f00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6756001600160a01b03165afa918215610d50575b6000918293610d1b575b50519081526020810191909152604090f35b81610d4192945061090b93503d8511610d49575b610d39818361055d565b810190613687565b929091610d09565b503d610d2f565b610d58613187565b610cff565b503461000e57602036600319011261000e57600435610d7b816105fe565b6001600160401b039081811691630784ce008311610ec357610d9b613f0c565b610da433612c49565b916003830180549083821680961115610eb1577f5cd2ddc163c3d93e18638ca23f2992d2a14b4ce98a66c629d85560b47c25f1c894610e6a848893610e4f610e2789610e1a610e0e610e889a610eac9d610dfe338c6148d2565b9b89031693429160401c16613fcd565b6001600160401b031690565b016001600160401b031690565b825467ffffffffffffffff60401b191660409190911b67ffffffffffffffff60401b16178255565b906001600160401b03166001600160401b0319825416179055565b610e7e84602084015160a085015101614831565b606083015261497d565b604080516001600160401b0395861681529490911660208501523393918291820190565b0390a2005b6040516305178a3b60e01b8152600490fd5b60405163897c6c7560e01b8152600490fd5b503461000e57600036600319011261000e57602060ff7f2967a798b92539a1b9eefe4d8eb931f96b68d27665e276f1bee2d5db7f7430495416604051908152f35b503461000e57600036600319011261000e57602061085f614b25565b503461000e576000806003193601126109db574681527fbb9c3660b51e1fafa886fcf600a68efa81371bd50359eccba98f0c4fff2cfeba60205260409020548015610f83575b602090604051908152f35b506020610f8e6130c6565b9050610f78565b503461000e57604036600319011261000e57600435610fb381610805565b610fd781610fc033612c82565b9060018060a01b0316600052602052604060002090565b54906024358201809211610ff257602091610a2a9133612d66565b60405163d256efb160e01b8152600490fd5b503461000e57600036600319011261000e5761101e613f0c565b600161102933612cbb565b0154801561110757620d2f00014211156110f55761104633612cbb565b54611075611063826000805160206154e5833981519152546133f1565b6000805160206154e583398151915255565b61108d61108133612cbb565b60016000918281550155565b6110c181337f0000000000000000000000006399c842dd2be3de30bf99bc7d1bbf6fa3650e706001600160a01b03166141a2565b60405190815233907f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364908060208101610eac565b60405163d7f0dd2d60e01b8152600490fd5b60405163b2ba9fb560e01b8152600490fd5b503461000e57604036600319011261000e57600435611137816104ae565b6024356001600160401b03811161000e5760209161115c610a2a9236906004016105b7565b906133fe565b9181601f8401121561000e578235916001600160401b03831161000e576020838186019501011161000e57565b90604060031983011261000e576004356111a8816104ae565b91602435906001600160401b03821161000e576111c791600401611162565b9091565b503461000e576111da3661118f565b6000805160206154c583398151915254600093926001600160a01b039291831633036109c95784927f00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6751690813b15611260578361124e95604051968795869485936342d65a8d60e01b8552600485016132fd565b03925af180156109b8576109ac575080f35b8380fd5b503461000e57600036600319011261000e576112b2611282336143f4565b337f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03166141a2565b005b503461000e57600036600319011261000e57602060405160008152f35b5060e036600319011261000e576004356112ea81610805565b602435906112f7826104ae565b6001600160401b0360443581811161000e576113179036906004016105b7565b50606435906084359061132982610805565b60a4359061133682610805565b60c4359181831161000e576114588561145d9381937f4a7c7bd7b3092a14241ec8138800e06869d4bf7ce2d955d6ada58e5ff3268a0a976114508b61138261ffff9a36906004016105b7565b9461138b613f0c565b6113958783614bc2565b6114446113a183612c49565b61142a6113ae85836148d2565b604051606087901b6bffffffffffffffffffffffff191660208201529c8d966113ee91906113e9896034810103601f199a8b8201815261055d565b61384a565b61140a6113ff8c60208401516133f1565b60a083015190612e11565b92610e7e600382019461142486546001600160401b031690565b90614831565b5460405198838a948360401c169216908c602086016136e1565b0390810186528561055d565b34938c61373d565b61371d565b6040519384529416926001600160a01b03169180602081015b0390a4005b503461000e57600036600319011261000e5760206000805160206154a583398151915254604051908152f35b503461000e57606036600319011261000e576004356114c5816104ae565b6024356001600160401b03811161000e57602091611516836114ee6115379436906004016105b7565b611503604435946114fe866105fe565b613318565b8260405194838680955193849201610872565b820190815203019020906001600160401b0316600052602052604060002090565b54604051908152f35b503461000e57602036600319011261000e5761090b60043561156181610805565b611591604091600060808451611576816104f1565b82815282602082015282868201528260608201520152612c49565b9060038151926115a0846104f1565b805484526001810154602085015260028101548385015201546001600160401b03908181166060850152821c1660808301525191829182919091608060a0820193805183526020810151602084015260408101516040840152816001600160401b0391826060820151166060860152015116910152565b503461000e576116263661061c565b91905030330361173f576020820161ffff80825116156000146116fa57835184019160a08584031261000e5761165c90516104ae565b60408401516001600160401b03811161000e576116a56116dc916020807f776434b505c7beb3db155c58df6c88985bf7c31730767e43ec773005059fed7a960191880101613275565b936114586060870151956116d460a060808a0151996116c38b6105fe565b0151916116cf836105fe565b613999565b9787896139be565b6040519384526001600160a01b039094169416918060208101611476565b60405162461bcd60e51b815260206004820152601c60248201527f4f4654436f72653a20756e6b6e6f776e207061636b65742074797065000000006044820152606490fd5b60405163f10781b160e01b8152600490fd5b503461000e57600036600319011261000e576040517f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03168152602090f35b503461000e57602036600319011261000e5760206115376004356117ba81610805565b612c10565b503461000e57602036600319011261000e576004356117dd33612c49565b906127106117f36117ed336144b2565b83613ed9565b04918282039182611802614b25565b1061196f577ffbd65cfd6de1493db337385c0712095397ecbd0504df64b861cdfceb80c7b422926118ab610eac93611838613f0c565b6118428433614bc2565b61184c33826148d2565b9061185785336138d0565b6000805160206154e5833981519152611871858254612e11565b9055876118f0575b610e7e61189861188d8760208601516133f1565b60a085015190612e11565b60038301546001600160401b0316611424565b6118bf6118b733612cbb565b918254612e11565b90554260016118cd33612cbb565b015560405191829133954291846040919493926060820195825260208201520152565b6119206118fc89613ea0565b61191a6000805160206154a5833981519152546040860151906133f1565b90613eec565b61193a600080516020615465833981519152918254612e11565b90557f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d66611968898254612e11565b9055611879565b60405163fde9a3e360e01b8152600490fd5b503461000e57602036600319011261000e5760206115376004356119a481610805565b612cf4565b503461000e57602036600319011261000e576004356119c781610805565b60009060016119df6119d883612cbb565b5492612cbb565b01549182611a09575b61090b90604051938493846040919493926060820195825260208201520152565b50620d2f0082016119e8565b503461000e57602036600319011261000e57600435611a3381610805565b6000805160206154c5833981519152546001600160a01b031633036109c95780611a5f6112b292612d2d565b61539c565b3590600182101561000e57565b503461000e57604036600319011261000e57600435600181101561000e57602435906001600160401b03821161000e576020611abf91611503611ab9839536906004016105b7565b91614dde565b82019081520301902054604051908152f35b503461000e5760031960403682011261000e57600435906001600160401b039081831161000e5760a090833603011261000e5760405190611b11826104f1565b826004013582526024830135611b2681610805565b60208301526044830135611b3981610805565b6040830152606483013590811161000e576112b292611b60608492600436918401016105b7565b60608401520135611b7081610805565b6080820152611b7d61060f565b9061403d565b6064359060ff8216820361000e57565b6084359060ff8216820361000e57565b503461000e5760c036600319011261000e57600435602435611bc4816105fe565b611bcc611b83565b917f0000000000000000000000006399c842dd2be3de30bf99bc7d1bbf6fa3650e706001600160a01b0316803b1561000e5781611c67916112b29560ff6040519163d505accf60e01b8352336004840152306024840152846044840152604435606484015216608482015260843560a482015260a43560c482015260008160e48183865af18015611c86575b611c6d575b5030903390613c21565b33614244565b80611c7a611c80926104d1565b80610867565b38611c5d565b611c8e613187565b611c58565b60011115611c9d57565b634e487b7160e01b600052602160045260246000fd5b60209081810182825283518091526040928383019281858460051b830101960194600080925b858410611ceb57505050505050505090565b9091929394959697603f19828203018852885180518252868101516001811015611d4057611d3083878a948594856001980152015190606090818a8201520190610895565b9a01980196959401929190611cd9565b634e487b7160e01b85526021600452602485fd5b503461000e5760208060031936011261000e57611d7b600435611d7681610805565b612d2d565b8054611d86816129c5565b91611d94604051938461055d565b8183526000908152838120938084015b838310611db9576040518061090b8782611cb3565b600382600192611dc889614ccd565b815201960192019194611da4565b503461000e57604036600319011261000e576112b2602435600435611dfa826105fe565b611c678130337f0000000000000000000000006399c842dd2be3de30bf99bc7d1bbf6fa3650e706001600160a01b0316613c21565b503461000e576000806003193601126109db5760405190807f2967a798b92539a1b9eefe4d8eb931f96b68d27665e276f1bee2d5db7f74304890815490611e7582612f22565b80865292600192808416908115611f055750600114611eab575b61090b86611e9f8188038261055d565b604051918291826108ba565b815292507f6626588b54b3522d930e3ee12195d0e9dddbde7c7325b02b73030918affce1c45b828410611eed575050508101602001611e9f8261090b38611e8f565b80546020858701810191909152909301928101611ed1565b905086955061090b96935060209250611e9f94915060ff191682840152151560051b820101929338611e8f565b503461000e5760208060031936011261000e57611f6e90611f5d600435611f58816104ae565b61334b565b611f75604091825194858092613031565b038461055d565b8251156120445782516013199384820190828211612037575b81611f9881612dfb565b1061202657818151106120155781611fc85750505061090b925080519160008352820181525b51918291826108ba565b839594955194601f8316801560051b91828289010195860101920101905b8084106120045750508352601f01601f1916815261090b9250611fbe565b815184529286019290860190611fe6565b835163aefe1d4360e01b8152600490fd5b8351632aaf81e960e01b8152600490fd5b61203f612de4565b611f8e565b5163ed2e153b60e01b8152600490fd5b503461000e57604036600319011261000e5761207f60043561207581610805565b6024359033612eec565b602060405160018152f35b503461000e576120993661118f565b6000805160206154c5833981519152549092906001600160a01b031633036109c9576040516020908484838301376120e66034828781013060601b8682015203601481018452018261055d565b6120ef8361334b565b918151916001600160401b038311612217575b612116836121108654612f22565b866133ac565b81601f84116001146121815750918061077a94927f8c0400cfe2d1199b1a725c78960bcc2a344d869b80590d0f2bd005db15a572ce9894600092612176575b50508160011b916000199060031b1c19161790555b604051938493846132fd565b015190503880612155565b9190601f19841661219786600052602060002090565b936000905b8282106121ff5750509260019285927f8c0400cfe2d1199b1a725c78960bcc2a344d869b80590d0f2bd005db15a572ce9a9661077a9896106121e6575b505050811b01905561216a565b015160001960f88460031b161c191690553880806121d9565b8060018697829497870151815501960194019061219c565b61221f6104ba565b612102565b503461000e57604036600319011261000e576020610a2a60043561224781610805565b6024359033612e1e565b503461000e57600036600319011261000e576040517f00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6756001600160a01b03168152602090f35b503461000e57602036600319011261000e57602061085f6004356122ba81610805565b614587565b503461000e57602036600319011261000e577ff8fad42e780bfa5459be3fe691e8ba1aec70342250112139c5771c3fd155f31260206004356122ff613f0c565b6123348130337f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316613c21565b60008051602061550583398151915280549082820180921161235b575b55604051908152a1005b612363612de4565b612351565b503461000e57600036600319011261000e5760206000805160206154e583398151915254604051908152f35b503461000e57602036600319011261000e576004356123b233612c49565b6003810180546001600160401b03429160401c16116124b557826123d4614b25565b1061196f57612450916123e5613f0c565b6123ef8433614bc2565b610e7e6123fc33836148d2565b9261240786336138d0565b6000805160206154e5833981519152612421878254612e11565b90556114246124426124378860208801516133f1565b60a087015190612e11565b91546001600160401b031690565b61245933612cbb565b612464828254612e11565b905542600161247233612cbb565b01556040805191825260006020830152429082015233907ffbd65cfd6de1493db337385c0712095397ecbd0504df64b861cdfceb80c7b422908060608101610eac565b6040516334e7720560e11b8152600490fd5b503461000e57608036600319011261000e576004356124e5816104ae565b6024356124f1816104ae565b6064356001600160401b03811161000e57612510903690600401611162565b6000805160206154c583398151915254919390916001600160a01b0390811633036109c9577f00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6751690813b1561000e57600080946125a4604051978896879586946332fb62e760e21b865261ffff809216600487015216602485015260443560448501526080606485015260848401916132dc565b03925af180156125c2575b6125b557005b80611c7a6112b2926104d1565b6125ca613187565b6125af565b506125d93661061c565b6125fe826125e8869496613318565b6020604051809288516115168184868d01610872565b54801561267857808251602084012003612667578461265b61077a9360006126537fc264d91f3adc5588250e1551f547752ca0cfa8f6b530d243b9f9f4cab10ea8e59961075561264d8a613318565b8b613499565b558686613544565b6040519485948561350e565b60405162451eff60e81b8152600490fd5b604051629c96ed60e61b8152600490fd5b503461000e57602036600319011261000e576004356126a781610805565b6126b081612c49565b60008051602061548583398151915254907f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d61541515806127a1575b61275e575b61273061271761270361274c9495612c10565b5460038401546001600160401b0316611424565b9361272a83549186600186015491614854565b90612e11565b9260026000805160206154658339815191525492015491614854565b60408051928352602083019190915290f35b61273061271761270361279761274c9561272a61278161277c613d82565b613ea0565b6000805160206154a58339815191525490613eec565b94505050506126f0565b506000805160206155058339815191525415156126eb565b503461000e5760e036600319011261000e576004356127d781610805565b6024356127e381610805565b604435906064356127f2611b93565b8142116129375760c06128b09261280887612cf4565b5490604051917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98352886020840152866040840152876060840152608083015260a082015220612881466000527fbb9c3660b51e1fafa886fcf600a68efa81371bd50359eccba98f0c4fff2cfeba602052604060002090565b549081156128f2575b6040519161190160f01b83526002830152602282015260c43591604260a4359220613194565b6001600160a01b038481169116036128e057826128cf6112b294612cf4565b6128d9815461316b565b9055612d66565b604051637570072b60e11b8152600490fd5b90506128fc6130c6565b9081612931466000527fbb9c3660b51e1fafa886fcf600a68efa81371bd50359eccba98f0c4fff2cfeba602052604060002090565b5561288a565b60405163384f7ce560e21b8152600490fd5b503461000e57602036600319011261000e57602061085f60043561296c81610805565b6144b2565b503461000e57600036600319011261000e57602061085f613d82565b503461000e57604036600319011261000e5760206115376004356129b081610805565b610fc0602435916129c083610805565b612c82565b6020906001600160401b0381116129de575b60051b0190565b6129e66104ba565b6129d7565b503461000e5760208060031936011261000e576001600160401b0360043581811161000e573660238201121561000e578060040135612a29816129c5565b92604090612a398251958661055d565b828552858501916024809460051b8601019436861161000e57848101935b868510612a67576112b288615117565b843584811161000e5782016060602319823603011261000e57835191612a8c8361050c565b878201358352612a9e60448301611a64565b8b84015260648201359286841161000e57612ac18c94938a8695369201016105b7565b86820152815201940193612a57565b503461000e57608036600319011261000e5761090b600435612af1816104ae565b60243590612afe826104ae565b612b09604435610805565b604051633d7b2f6f60e21b815261ffff91821660048201529116602482015230604482015260648035908201526000816084817f00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6756001600160a01b03165afa908115612ba6575b600091612b85575b50604051918291826108ba565b612ba0913d8091833e612b98818361055d565b8101906132b7565b38612b78565b612bae613187565b612b70565b503461000e57600036600319011261000e5760008051602061550583398151915254612bdd613d82565b7f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d6654604080519290930382526020820152f35b6001600160a01b031660009081527fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b46020526040902090565b6001600160a01b031660009081527f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d636020526040902090565b6001600160a01b031660009081527fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b56020526040902090565b6001600160a01b031660009081527f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d5f6020526040902090565b6001600160a01b031660009081527fbb9c3660b51e1fafa886fcf600a68efa81371bd50359eccba98f0c4fff2cfeb96020526040902090565b6001600160a01b031660009081527fedf5b05b5f3c4999fbcd4c981b71eaaa791221cf2deabf009ac3784dd586d35a6020526040902090565b6001600160a01b038082169291908315612dd2578216938415612dc15780612db47f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92594610fc0602095612c82565b55604051908152a3600190565b6040516262920b60e91b8152600490fd5b604051635a68b7ab60e01b8152600490fd5b50634e487b7160e01b600052601160045260246000fd5b90601f8201809211612e0957565b61058b612de4565b91908201809211612e0957565b6001600160a01b038082169291908315612eda578216938415612ec857612e458383613650565b612e4e82612c10565b54808211612eb6577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef93612eae93612e8b84612e91940391612c10565b55612c10565b612e9c828254612e11565b90556040519081529081906020820190565b0390a3600190565b604051637183160b60e01b8152600490fd5b6040516320a2645160e21b8152600490fd5b6040516368551d5f60e01b8152600490fd5b90612efa81610fc084612c82565b5492838111612f1157612f0e930391612d66565b50565b60405162e14ccf60e11b8152600490fd5b90600182811c92168015612f52575b6020831014612f3c57565b634e487b7160e01b600052602260045260246000fd5b91607f1691612f31565b906000917f2967a798b92539a1b9eefe4d8eb931f96b68d27665e276f1bee2d5db7f743047908154612f8d81612f22565b8083529260019180831690811561300c5750600114612fad575b50505050565b90929394506000527fc3d5862a69f8ca70e4828043821a8d13dfd34088ddb52ba52f4c8aedc2ea63b5916000925b848410612ff45750506020925001019038808080612fa7565b80546020858501810191909152909301928101612fdb565b92505050602093945060ff929192191683830152151560051b01019038808080612fa7565b906000929180549161304283612f22565b9182825260019384811690816000146130a357506001146130635750505050565b90919394506000526020928360002092846000945b83861061308f575050505001019038808080612fa7565b805485870183015294019385908201613078565b9294505050602093945060ff191683830152151560051b01019038808080612fa7565b6040516130d6816108f081612f5c565b60208151910120603160f81b60206040516130f081610527565b60018152015260405160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815261316581610542565b51902090565b600190600019811461317b570190565b613183612de4565b0190565b506040513d6000823e3d90fd5b9291927f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116132635760ff8216601b8114159081613257575b5061324557613202600093602095604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa15613238575b6000516001600160a01b038116156132265790565b6040516347f3b32760e11b8152600490fd5b613240613187565b613211565b604051630acd54a960e41b8152600490fd5b601c91501415386131ce565b60405163e4a61add60e01b8152600490fd5b81601f8201121561000e57805161328b8161058d565b92613299604051948561055d565b8184526020828401011161000e5761067a9160208085019101610872565b9060208282031261000e5781516001600160401b03811161000e5761067a9201613275565b908060209392818452848401376000828201840152601f01601f1916010190565b60409061ffff61067a959316815281602082015201916132dc565b61ffff166000527f090abe8f4a9a553a6a365135ae432dc8ce193b30c76d2c3ec06109c134e9b111602052604060002090565b61ffff166000527f32b0ffe8e22ea97a097b9fe1753a56cad70a64a02675ba49c9f49d75c2b80bb8602052604060002090565b50634e487b7160e01b600052600060045260246000fd5b8181106133a0575050565b60008155600101613395565b9190601f81116133bb57505050565b61058b926000526020600020906020601f840160051c830193106133e7575b601f0160051c0190613395565b90915081906133da565b91908203918211612e0957565b9061342161340e61341a9361334b565b60405193848092613031565b038361055d565b805182518082149384613452575b8461343c575b5050505090565b6020929394508201209201201438808080613435565b811515945061342f565b9061348561067a959361ffff6001600160401b0393168452608060208501526080840190610895565b931660408201526060818403910152610895565b6020906134b3928260405194838680955193849201610872565b82019081520301902090565b936001600160401b036134ec6135009461ffff61067a99979516885260a0602089015260a0880190610895565b921660408601528482036060860152610895565b916080818403910152610895565b9061353960609361ffff6001600160401b039398979698168452608060208501526080840190610895565b951660408201520152565b91906020820161ffff80825116156000146116fa57835184019160a08584031261000e5761357290516104ae565b60408401516001600160401b03811161000e576116a56135bb916020807f776434b505c7beb3db155c58df6c88985bf7c31730767e43ec773005059fed7a960191880101613275565b6040519384526001600160a01b0394909416941691602090a4565b90929160008091604051956135ea87610542565b6096875282602088019560a036883760208451940192f1903d9060968211613618575b6000908286523e9190565b6096915061360d565b6001600160a01b0316158015613648575b61058b576040516345de49e960e01b8152600490fd5b506001613632565b6001600160a01b039081161591821561367c575b505061058b576040516345de49e960e01b8152600490fd5b161590503880613664565b919082604091031261000e576020825192015190565b919261067a959361ffff6136cc9316845260018060a01b0316602084015260a0604084015260a0830190610895565b92151560608201526080818403910152610895565b92909361370160809396956000865260a0602087015260a0860190610895565b9560408501526001600160401b03809216606085015216910152565b61373590602060405192828480945193849201610872565b810103902090565b9461375d939194956137646137518261334b565b60405196878092613031565b038661055d565b845115613838576001600160a01b037f00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd675811694909390853b1561000e576137de986000988661380c9461ffff986137f16040519e8f9d8e9c8d9b62c5803160e81b8d521660048c015260c060248c015260c48b0190610895565b60031995868b83030160448c0152610895565b951660648801521660848601528483030160a4850152610895565b03925af1801561382b575b61381e5750565b80611c7a61058b926104d1565b613833613187565b613817565b604051631afd825f60e31b8152600490fd5b90336001600160a01b03831603613865575b61058b916138d0565b61386e33612c82565b3360009081526020829052604090205492908383116138be576138ae6138b6918461058b96039182913360018060a01b0316600052602052604060002090565b553383612d66565b50915061385c565b60405163b09ec56160e01b8152600490fd5b6001600160a01b038116908115613987576138ea81613621565b6138f381612c10565b5480841161397557837fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9261392e6000966020940391612c10565b557fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b6805490828203918211613968575b55604051908152a3565b613970612de4565b61395e565b60405163b1d35b2360e01b8152600490fd5b6040516378f1627760e11b8152600490fd5b60148151106139ac576020015160601c90565b60405163aefe1d4360e01b8152600490fd5b91906139c8613f0c565b6139d183612c49565b906139dc84836148d2565b906003830193816139f886546001600160401b039060401c1690565b916001600160401b0380809a166000428211600014613b535750429003935b16600042821115613b4b5750429003905b60a086019384518401926020880193845191613a43936141fe565b6001600160401b0316428b16016001600160401b0316885467ffffffffffffffff60401b1916604082901b67ffffffffffffffff60401b161789559988546001600160401b03169085518501908085519316931692613aa1936141fe565b6001600160401b0316875467ffffffffffffffff19166001600160401b0390911617875551019051018454613adc906001600160401b031690565b613ae591614831565b6060830152613af49085613b5b565b613afd9161497d565b54604080516001600160401b0392831681529390911660208401526001600160a01b0391909116917f89bf27326cff6f41c8f98732f3100cce6cd16f1ae76bb214139d60e283809f449190a2565b905090613a28565b905093613a17565b6001600160a01b0316908115613c0f577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000927fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b6805490828201809211613c02575b558484527fc991b2e918acaba8e5721668ed0b1982684e5a8692a621bcd2d7ef326bb015b48252604084208054908282018092116139685755604051908152a3565b613c0a612de4565b613bc0565b60405163da007acd60e01b8152600490fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815261058b91613c63826104f1565b613cac9160405191613c7483610527565b60208084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908401526001600160a01b0316613ce9565b805180613cb7575050565b816020918101031261000e5760200151613cd081610bc6565b15613cd757565b6040516306dacbc160e51b8152600490fd5b9190606092803b15613d705781600092918360208194519301915af1913d15613d6a57503d91613d188361058d565b92613d26604051948561055d565b83523d6000602085013e5b15613d3a575090565b815115613d4a5750805190602001fd5b60405162461bcd60e51b8152908190613d6690600483016108ba565b0390fd5b91613d31565b604051632270d6bf60e21b8152600490fd5b600080516020615505833981519152547f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d61544203428111613e93575b600160801b906f7ffffdbc2dee24e1000000000000000090805b613e2457505090613e0d8161067a9360401c60016001607f1b031981121580613e13575b613e059061469a565b600f0b6146a1565b906133f1565b5060016001607f1b03811315613dfc565b60018116613e87575b607f918002821c60028216613e7c575b8002821c91600492838316613e71575b8002811c60088316613e66575b8002901c911c80613dd8565b809402811c93613e5a565b809402811c93613e4d565b809302821c92613e3d565b918102607f1c91613e2d565b613e9b612de4565b613dbe565b906c0c9f2c9cd04674edea4000000091828102928184041490151715612e0957565b90610bb891828102928184041490151715612e0957565b81810292918115918404141715612e0957565b8115613ef6570490565b634e487b7160e01b600052601260045260246000fd5b7f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d618054158015613fb6575b8015613f9f575b613f9a57613f4a613d82565b613f5661278182613ea0565b6000805160206154858339815191528054918201809211613f8d575b55600080516020615505833981519152908154039055429055565b613f95612de4565b613f72565b429055565b506000805160206155058339815191525415613f3e565b506000805160206154a58339815191525415613f37565b9080821115613fda575090565b905090565b9081602091031261000e575190565b9591909460c095614036949599989960018060a01b0396878095818095168c521660208b015260408a015216606088015216608086015260e060a086015260e0850190610895565b9416910152565b90614047336143f4565b91821561419d577f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48926020906001600160a01b03907f000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb5906140ab8183858a166141a2565b848401516000906001600160a01b031660408701519094906001600160a01b031660608801516080890151919a9161412791906001600160a01b0316906040519c8d998a988996632e25a7ab60e01b88527f0000000000000000000000006399c842dd2be3de30bf99bc7d1bbf6fa3650e709060048901613fee565b0393165af1928315614190575b600093614160575b5051821061414e5761058b9133614244565b604051630c96bfff60e41b8152600490fd5b61418291935060203d8111614189575b61417a818361055d565b810190613fdf565b913861413c565b503d614170565b614198613187565b614134565b505050565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815261058b91608082016001600160401b038111838210176141f1575b604052613c63565b6141f96104ba565b6141e9565b92614216816142108561067a97613ed9565b93613ed9565b8201809211614237575b820180921115613eec57614232612de4565b613eec565b61423f612de4565b614220565b9190916001600160401b039182811690630784ce008211610ec357838042169182011690614270613f0c565b61427984612c49565b918661428586856148d2565b926003850195886142a188546001600160401b039060401c1690565b946000428211156143ec5750429003945b166000428211156143e45750429003915b60a0860194855185019360208801948551916142de936141fe565b6001600160401b0316016001600160401b0316875467ffffffffffffffff60401b1916604082901b67ffffffffffffffff60401b161788559887546001600160401b031690855185019084519216614335936141fe565b6001600160401b0316865467ffffffffffffffff19166001600160401b0390911617865551019051018354614370906001600160401b031690565b61437991614831565b60608201526143888685613b5b565b6143919161497d565b54604080519485526001600160401b0391821660208601529216918301919091526001600160a01b0316907fd9033c965af127d7b3cd5e17d24e62287aec96a7a01a26a1aded18f1fa4765d090606090a2565b9050916142c3565b9050946142b2565b906143fd613f0c565b61440682612c49565b9161445361441482856148d2565b60a081018051156144a25761444790602083015190518101809111614495575b60038701546001600160401b0316611424565b60608201525b8461497d565b825460009093556040518381526001600160a01b0391909116907fc9695243a805adb74c91f28311176c65b417e842d5699893cef56d18bfa48cba90602090a2565b61449d612de4565b614434565b506040810151606082015261444d565b60036144c56001600160401b0392612c49565b015460401c16428111156144f3576109c46301e1338091429003020490611d4c8083116144ef5750565b9150565b6040516399067dc360e01b8152600490fd5b50634e487b7160e01b600052603260045260246000fd5b60209080511561452a570190565b613183614505565b60409080516001101561452a570190565b60609080516002101561452a570190565b60809080516003101561452a570190565b602091815181101561457a575b60051b010190565b614582614505565b614572565b6145b061459382612c10565b5461142460036145a285612c49565b01546001600160401b031690565b903b61466c576145be6146f7565b9081519060005b8281106145e557506020926145df92600019019150614565565b51015190565b6145ef8185614565565b518051831061460157506001016145c5565b919392506000811561465657506131839293612710602061463561464d946000190161462d818a614565565b515198614565565b510151955b8087602087015103955103920302613eec565b02612710900490565b613183935061464d91509361271060009561463a565b6000805160206154a58339815191525460011c80821061468e575050610bb890565b61423261067a92613ec2565b1561000e57565b81156146f057600f0b6000908181126109c5576fffffffffffffffffffffffffffffffff8316810260401c9260801c026001600160c01b0381116109c55760401b90821982116109db57500190565b5050600090565b604090815191614706836104f1565b600483526000805b608081106147dd57505050612f0e8261472561057e565b69010f0cf064dd5920000081526103e860208201526147438261451c565b5261474d8161451c565b5061475661057e565b690a968163f0a57b40000081526109c4602082015261477482614532565b5261477e81614532565b5061478761057e565b6969e10de76676d08000008152610dac60208201526147a582614543565b526147af81614543565b506147b861057e565b6a0211654585005212800000815261177060208201526147d782614554565b52614554565b60209083516147eb81610527565b83815282848183015282880101520161470e565b801561482a57630784ce00811015614823576127106301e133809102046109c40190565b5061a60490565b506109c490565b6148509061484a6001600160401b0361271094166147ff565b90613ed9565b0490565b6c0c9f2c9cd04674edea400000009161486c91613ed9565b0490810390811161487a5790565b61067a612de4565b6040519060c082018281106001600160401b038211176148c5575b6040528160a06000918281528260208201528260408201528260608201528260808201520152565b6148cd6104ba565b61489d565b614952906148de614882565b506148fc6148ea614882565b6001600160a01b038516815293612c10565b548060208501528061495a575b50600080516020615485833981519152549061493060408501928351600184015491614854565b6080850152600260008051602061546583398151915254925191015491614854565b60a082015290565b614972906001600160401b0360038401541690614831565b604084015238614909565b604061058b9260008051602061548583398151915254926149a46060830194855190614b0d565b60018201556149c460008051602061546583398151915254855190614b0d565b600282015560018060a01b0382511690608083015160a0840151918054918201809211614a77575b55806149ff575b50500151905190614a84565b60207f7fe22991e338d21d6aa37b281a268bca0c6a3c44451c34cc7dc9f42cb6ab9ecc917f93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d66805490828203918211614a6a575b55614a5d8185613b5b565b8551908152a238806149f3565b614a72612de4565b614a52565b614a7f612de4565b6149ec565b80821115614acc578103908111614abf575b6000805160206154a58339815191528054918201809211614ab45755565b614abc612de4565b55565b614ac7612de4565b614a96565b90818110614ad8575050565b8103908111614b00575b6000805160206154a58339815191528054918203918211614ab45755565b614b08612de4565b614ae2565b614850906c0c9f2c9cd04674edea4000000092613ed9565b6040516370a0823160e01b81523060048201526020816024817f0000000000000000000000006399c842dd2be3de30bf99bc7d1bbf6fa3650e706001600160a01b03165afa908115614bb5575b600091614b97575b506000805160206154e583398151915254810390811161487a5790565b614baf915060203d81116141895761417a818361055d565b38614b7a565b614bbd613187565b614b72565b90916001600160401b0390614bf7614be1600395848761085688612c49565b92614beb85612c10565b54908661085687612c49565b92600092614c0482612d2d565b95865490614c11826129c5565b97614c1f604051998a61055d565b8289526020808a0191885280882088925b858410614c8757505050505050835b8651851015614c6057600190614c558689614565565b515101940193614c3f565b9490919350614c709295506133f1565b808311614c7c57505050565b61058b920390614e32565b8483600192614c9585614ccd565b815201920193019290614c30565b8054821015614cc0575b6000526003602060002091020190600090565b614cc8614505565b614cad565b90604051606081018181106001600160401b03821117614d24575b60405280928054825260ff600182015416916001831015611c9d57614d206040926002946020840152611f6e84518096819301613031565b0152565b614d2c6104ba565b614ce8565b80548015614dc85760001901906002614d4a8383614ca3565b614dbb575b600081556000600182015501614d658154612f22565b9081614d7057505055565b601f8211600114614d8357600091505555565b614dab614abc926001601f614d9d85600052602060002090565b920160051c82019101613395565b6000908082528160208120915555565b614dc361337e565b614d4f565b634e487b7160e01b600052603160045260246000fd5b6001811015611c9d576000527fedf5b05b5f3c4999fbcd4c981b71eaaa791221cf2deabf009ac3784dd586d35b602052604060002090565b929190614e2d602091604086526040860190610895565b930152565b614e3b81612d2d565b918254805b614e4a5750505050565b60001901614e61614e5b8286614ca3565b50614ccd565b918251808210600014614f1a57508080614e7b8488614ca3565b508181540390555b03927f0690a72b405552454fdede08f841412e53cde7110c70cbe1e8676c84ef6d0ff9614f066020830193614ec18551614ebc81611c93565b614dde565b94614ed26040809601968751613499565b8281540390555194614ee386611c93565b5193614eee86611c93565b516001600160a01b0389169490928392909183614e16565b0390a38115614f155780614e40565b612fa7565b8091614f2587614d31565b614e83565b602090818184031261000e578051906001600160401b03821161000e57019180601f8401121561000e578251614f5f816129c5565b93614f6d604051958661055d565b818552838086019260051b82010192831161000e578301905b828210614f94575050505090565b8380918351614fa281610805565b815201910190614f86565b602081519101519060208110614fc1575090565b6000199060200360031b1b1690565b614ff481546801000000000000000081101561510a575b6001928382018155614ca3565b6150fd575b82518155818101602091828501519084821015611c9d5760409260029260ff8019835416911617905501930151908151916001600160401b0383116150f0575b61504d836150478754612f22565b876133ac565b81601f8411600114615086575092829391839260009461507b575b50501b916000199060031b1c1916179055565b015192503880615068565b919083601f19811661509d88600052602060002090565b946000905b888383106150d657505050106150bd575b505050811b019055565b015160001960f88460031b161c191690553880806150b3565b8587015188559096019594850194879350908101906150a2565b6150f86104ba565b615039565b61510561337e565b614ff9565b6151126104ba565b614fe7565b9061513361512433612c10565b5461142460036145a233612c49565b9061513d33612d2d565b615147338261539c565b604093845191636a0ee5f560e11b8352600090600460018060a01b0396838683818b7f0000000000000000000000004f273f4efa9ecf5dd245a338fad9fe0bab63b350165afa95861561538f575b849661536b575b50959294938598979894865b825188101561535e576151c86151be8985614565565b5197885190612e11565b9a898c1161534e57602088019687516151e081611c93565b6151e981611c93565b1580159061533f575b61532f578b9c61522661521a61521a6152149d9e9f8d999799019d8e51614fad565b60601c90565b6001600160a01b031690565b95809d84829816975b875181101561531d5761525561521a615248838b614565565b516001600160a01b031690565b8914615269576152649061316b565b61522f565b509650989099919b93949a929c5060015b1561530d57907f0fb3ba678f396dab592d016f21ea8ea2e919c6463c0de53fe8a8613bd53222296152f88f6153009695946152b5858d614fd0565b84516152d26118b76152cb8451614ebc81611c93565b8951613499565b905551946152df86611c93565b519351906152ec86611c93565b51918291339583614e16565b0390a361316b565b96909197949998996151a8565b8d516321cdded960e21b81528790fd5b509650989099919b93949a929c61527a565b8b5163350e118b60e21b81528690fd5b5060158c8a01515114156151f2565b8a5163016a795f60e71b81528590fd5b5050505050945050505050565b6153889196503d8086833e615380818361055d565b810190614f2a565b943861519c565b615397613187565b615195565b908154805b6153aa57505050565b600019016153bb614e5b8285614ca3565b8051907f0690a72b405552454fdede08f841412e53cde7110c70cbe1e8676c84ef6d0ff961544560208301936153f58551614ebc81611c93565b94604091615407838701978851613499565b8054918203918211615457575b55519461542086611c93565b5193519061542d86611c93565b516001600160a01b0388169490928392909183614e16565b0390a38061545284614d31565b6153a1565b61545f612de4565b61541456fe93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d6593fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d6493fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d628a22373512790c48b83a1fe2efdd2888d4a917bcdc24d0adf63e60f67168046093fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d5d93fc9d7292a51a532b02476d8b2b9af907a368e01a95aad9b65afa72ad771d60a2646970667358221220023a2bdf111b01ce6c0419344d77c23bbdcf10fc37e7894cde7947eda28ac2f064736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000004f273f4efa9ecf5dd245a338fad9fe0bab63b35000000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd6750000000000000000000000006399c842dd2be3de30bf99bc7d1bbf6fa3650e70000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb5
-----Decoded View---------------
Arg [0] : proxyManager (address): 0x4F273F4Efa9ECF5Dd245a338FAd9fe0BAb63B350
Arg [1] : lzEndpoint (address): 0x66A71Dcef29A0fFBDBE3c6a460a3B5BC225Cd675
Arg [2] : premia (address): 0x6399C842dD2bE3dE30BF99Bc7D1bBF6Fa3650E70
Arg [3] : rewardToken (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [4] : exchangeHelper (address): 0x380Eb51db6FE77a8876cB0735164cB8AF7f80Cb5
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000004f273f4efa9ecf5dd245a338fad9fe0bab63b350
Arg [1] : 00000000000000000000000066a71dcef29a0ffbdbe3c6a460a3b5bc225cd675
Arg [2] : 0000000000000000000000006399c842dd2be3de30bf99bc7d1bbf6fa3650e70
Arg [3] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [4] : 000000000000000000000000380eb51db6fe77a8876cb0735164cb8af7f80cb5
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.