ETH Price: $3,256.50 (+0.57%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

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
File 1 of 64 : VxPremia.sol
// 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);
    }
}

File 2 of 64 : IOwnableInternal.sol
// 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();
}

File 3 of 64 : OwnableInternal.sol
// 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;
    }
}

File 4 of 64 : OwnableStorage.sol
// 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
        }
    }
}

File 5 of 64 : ECDSA.sol
// 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)
            );
    }
}

File 6 of 64 : IERC165.sol
// 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);
}

File 7 of 64 : IERC165Internal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { IERC165Internal } from './IERC165Internal.sol';

/**
 * @title ERC165 interface registration interface
 */
interface IERC165Internal {

}

File 8 of 64 : IERC173.sol
// 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;
}

File 9 of 64 : IERC173Internal.sol
// 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
    );
}

File 10 of 64 : IERC20.sol
// 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);
}

File 11 of 64 : IERC20Internal.sol
// 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
    );
}

File 12 of 64 : ERC165Base.sol
// 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);
    }
}

File 13 of 64 : ERC165BaseInternal.sol
// 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;
    }
}

File 14 of 64 : ERC165BaseStorage.sol
// 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
        }
    }
}

File 15 of 64 : IERC165Base.sol
// 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 {}

File 16 of 64 : IERC165BaseInternal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IERC165Internal } from '../../../interfaces/IERC165Internal.sol';

interface IERC165BaseInternal is IERC165Internal {
    error ERC165Base__InvalidInterfaceId();
}

File 17 of 64 : ERC20Base.sol
// 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);
    }
}

File 18 of 64 : ERC20BaseInternal.sol
// 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 {}
}

File 19 of 64 : ERC20BaseStorage.sol
// 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
        }
    }
}

File 20 of 64 : IERC20Base.sol
// 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 {

}

File 21 of 64 : IERC20BaseInternal.sol
// 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();
}

File 22 of 64 : ERC20Extended.sol
// 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);
    }
}

File 23 of 64 : ERC20ExtendedInternal.sol
// 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;
    }
}

File 24 of 64 : IERC20Extended.sol
// 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);
}

File 25 of 64 : IERC20ExtendedInternal.sol
// 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();
}

File 26 of 64 : ISolidStateERC20.sol
// 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
{}

File 27 of 64 : ERC20Metadata.sol
// 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();
    }
}

File 28 of 64 : ERC20MetadataInternal.sol
// 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;
    }
}

File 29 of 64 : ERC20MetadataStorage.sol
// 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
        }
    }
}

File 30 of 64 : IERC20Metadata.sol
// 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);
}

File 31 of 64 : IERC20MetadataInternal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title ERC20 metadata internal interface
 */
interface IERC20MetadataInternal {

}

File 32 of 64 : ERC20Permit.sol
// 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);
    }
}

File 33 of 64 : ERC20PermitInternal.sol
// 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);
    }
}

File 34 of 64 : ERC20PermitStorage.sol
// 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
        }
    }
}

File 35 of 64 : IERC20Permit.sol
// 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 {

}

File 36 of 64 : IERC20PermitInternal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { IERC2612Internal } from './IERC2612Internal.sol';

interface IERC20PermitInternal is IERC2612Internal {
    error ERC20Permit__ExpiredDeadline();
    error ERC20Permit__InvalidSignature();
}

File 37 of 64 : IERC2612.sol
// 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;
}

File 38 of 64 : IERC2612Internal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

interface IERC2612Internal {}

File 39 of 64 : SolidStateERC20.sol
// 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
{

}

File 40 of 64 : AddressUtils.sol
// 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);
        }
    }
}

File 41 of 64 : Math.sol
// 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;
        }
    }
}

File 42 of 64 : SafeERC20.sol
// 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();
        }
    }
}

File 43 of 64 : UintUtils.sol
// 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);
    }
}

File 44 of 64 : ABDKMath64x64.sol
// 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);
      }
    }
  }
}

File 45 of 64 : IProxyManager.sol
// SPDX-License-Identifier: LGPL-3.0-or-later

pragma solidity ^0.8.0;

interface IProxyManager {
    function getPoolList() external view returns (address[] memory);
}

File 46 of 64 : IExchangeHelper.sol
// 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);
}

File 47 of 64 : ILayerZeroEndpoint.sol
// 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);
}

File 48 of 64 : ILayerZeroReceiver.sol
// 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;
}

File 49 of 64 : ILayerZeroUserApplicationConfig.sol
// 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;
}

File 50 of 64 : LzApp.sol
// 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);
    }
}

File 51 of 64 : LzAppStorage.sol
// 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
        }
    }
}

File 52 of 64 : NonblockingLzApp.sol
// 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];
    }
}

File 53 of 64 : NonblockingLzAppStorage.sol
// 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
        }
    }
}

File 54 of 64 : IOFT.sol
// 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();
}

File 55 of 64 : IOFTCore.sol
// 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);
}

File 56 of 64 : OFT.sol
// 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);
    }
}

File 57 of 64 : OFTCore.sol
// 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;
}

File 58 of 64 : BytesLib.sol
// 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;
    }
}

File 59 of 64 : ExcessivelySafeCall.sol
// 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)
        }
    }
}

File 60 of 64 : IPremiaStaking.sol
// 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);
}

File 61 of 64 : IVxPremia.sol
// 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;
}

File 62 of 64 : PremiaStaking.sol
// 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;
    }
}

File 63 of 64 : PremiaStakingStorage.sol
// 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
        }
    }
}

File 64 of 64 : VxPremiaStorage.sol
// 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
        }
    }
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"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"}]

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


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.