ETH Price: $2,435.71 (+5.41%)

Contract

0x2d68676c40Bf820Eb84e3B7d8d77a1C473b08B61
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x61014060179694782023-08-22 9:51:59394 days ago1692697919IN
 Create: CrossChainSwaps
0 ETH0.1334369226.72047521

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CrossChainSwaps

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 17500 runs

Other Settings:
default evmVersion
File 1 of 29 : CrossChainSwaps.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {
    CrossChainSwapsSignatureUtil
} from "./CrossChainSwapsSignatureUtil.sol";
import {
    OwnableUpgradeable,
    Initializable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {
    ReentrancyGuardUpgradeable
} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {
    PausableUpgradeable
} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import {
    AssetType,
    TradeOffer,
    TradeDetailed,
    Status,
    TradeAcceptMessage,
    PayloadType,
    Fees,
    TradeResultMessage,
    Result,
    Trade,
    TradeConstants,
    SELLER_BITMASK,
    BUYER_BITMASK,
    ForwardingGas
} from "../utils/DataTypes.sol";
import { CrossChainSwapsStorage } from "./CrossChainSwapsStorage.sol";
import { IWormholeReceiver } from "../interfaces/IWormholeReceiver.sol";
import { WormholeInteraction } from "./WormholeInteraction.sol";
import { Encoder, Decoder } from "../libraries/Formatters.sol";
import { IVault } from "../interfaces/IVault.sol";

/// @title CrossChainSwaps
/// @author NF3 Exchange
/// @notice This contract inherits from WormholeInteractions, CrossChainSwapsSignatureUtil
///         contracts and implements IWormholeReceiver interface.
/// @dev This contract acts as the public facing functions that the users as well as relayers
///      directly interact with

contract CrossChainSwaps is
    Initializable,
    OwnableUpgradeable,
    ReentrancyGuardUpgradeable,
    PausableUpgradeable,
    CrossChainSwapsSignatureUtil,
    WormholeInteraction,
    IWormholeReceiver
{
    /* ===== INIT ===== */

    function initialize(
        address wormholeRelayer_,
        uint16 wormholeChainId_,
        uint8 consistencyLevel_
    ) public initializer {
        _setWormholeRelayer(wormholeRelayer_);
        _setConsistencyLevel(consistencyLevel_);
        _setWormholeChainId(wormholeChainId_);

        __Ownable_init();
        __Pausable_init();
        __ReentrancyGuard_init();
        __EIP712_init("NF3 Crosschain Swaps", "0.2.0");
    }

    /// -----------------------------------------------------------------------
    /// Modifiers
    /// -----------------------------------------------------------------------

    modifier onlyRelayer() {
        _onlyRelayer();
        _;
    }

    /// -----------------------------------------------------------------------
    /// Trade actions
    /// -----------------------------------------------------------------------

    /// @notice Inherit from ICrossChainSwaps
    function cancelTrade(
        TradeOffer calldata _offer,
        bytes memory _signature
    ) external override whenNotPaused {
        // verify signature
        _verifyTradeOfferSignature(_offer, _signature);

        // should be called by the owner
        if (_offer.owner != _msgSender()) {
            revert SwapError(SwapErrorCode.OFFER_OWNER_ONLY);
        }

        // check nonce
        if (getNonce(_offer.owner, _offer.nonce)) {
            revert SwapError(SwapErrorCode.INVALID_OFFER_NONCE);
        }

        // set nonce
        _setNonce(_offer.owner, _offer.nonce);

        emit TradeOfferCancelled(_offer);
    }

    /// @notice Inherit from ICrossChainSwaps
    function initializeTrade(
        TradeOffer calldata _offer,
        bytes memory offerSignature,
        ForwardingGas calldata _forwardingGas,
        bytes memory gasSignature
    )
        external
        payable
        override
        whenNotPaused
        nonReentrant
        returns (uint64 sequence)
    {
        // perform sanity check
        _initTradeSanityChecks(
            _offer,
            offerSignature,
            _forwardingGas,
            gasSignature
        );

        // get hash of the trade offer
        bytes32 tradeOfferHash = Encoder.hashTradeOffer(_offer);

        // build trade struct
        TradeDetailed memory trade = TradeDetailed({
            tradeOfferHash: tradeOfferHash,
            offeringAssets: _offer.offeringAssets.primaryChainAssets,
            considerationAssets: _offer.considerationAssets.primaryChainAssets,
            seller: _offer.owner,
            buyer: _msgSender(),
            status: Status.PENDING,
            withdrawBitmap: 0,
            recoveryRequested: false
        });

        // make external calls to pull funds etc
        IVault(_state.vaultAddress).receiveAssets(
            trade,
            _offer.buyerFees,
            _offer.sellerFees
        );

        // invalidate nonce
        _setNonce(_offer.owner, _offer.nonce);
        _setGasNonce(_forwardingGas.nonce);

        // store trade data to storage corrsoponding to it's hash
        _setTradeData(trade);

        bytes memory payload = Encoder.encodeTradeAcceptMessage(
            TradeAcceptMessage({
                payloadType: PayloadType.PROCESS_TRADE,
                tradeOfferHash: tradeOfferHash,
                offeringAssets: _offer.offeringAssets.secondaryChainAssets,
                considerationAssets: _offer
                    .considerationAssets
                    .secondaryChainAssets,
                seller: trade.seller,
                buyer: trade.buyer,
                primaryChainId: uint16(_offer.primaryChainId),
                secondaryChainId: uint16(_offer.secondaryChainId)
            })
        );

        // emit message and request delivery
        sequence = _sendWormholeMessage(
            payload,
            _offer.offeringAssets.secondaryChainAssets,
            _offer.considerationAssets.secondaryChainAssets,
            _state.targetChainIdToContract[uint16(_offer.secondaryChainId)],
            uint16(_offer.secondaryChainId),
            uint16(_offer.primaryChainId),
            _forwardingGas.forwardingValue
        );

        // emit custom event
        emit TradeInitialized(
            _offer,
            tradeOfferHash,
            trade,
            _msgSender(),
            _offer.owner,
            sequence
        );
    }

    /// @notice Inherit from ICrossChainSwaps
    function requestRecovery(
        bytes32 tradeOfferHash,
        TradeConstants calldata tradeConstants
    ) external override {
        Trade storage trade = _state.trades[tradeOfferHash];

        // check if trade exist and is in correct state
        if (trade.status != Status.PENDING) {
            revert SwapError(SwapErrorCode.INVALID_TRADE_STATUS);
        }

        // check if correct trade constants are passed
        _checkTradeConstants(trade, tradeConstants);

        if (
            _msgSender() != tradeConstants.seller &&
            _msgSender() != tradeConstants.buyer
        ) // check if called by buyer or seller, get their bit
        {
            revert SwapError(SwapErrorCode.TRADE_OWNERS_ONLY);
        }

        if (!trade.recoveryRequested) {
            // mark trade as requsted recovery
            trade.recoveryRequested = true;

            // emit event
            emit RecoveryRequested(tradeOfferHash);
        }
    }

    /// @notice Inherit from ICrossChainSwaps
    function recoverTrade(
        bytes32 tradeOfferHash,
        Result result
    ) external override onlyOwner {
        Trade storage trade = _state.trades[tradeOfferHash];

        // check if trade exist, is in correct state
        if (trade.status != Status.PENDING) {
            revert SwapError(SwapErrorCode.INVALID_TRADE_STATUS);
        }

        //check if recovery is requested
        if (!trade.recoveryRequested) {
            revert SwapError(SwapErrorCode.RECOVERY_NOT_REQUESTED);
        }

        // set the status of the trade
        trade.status = result == Result.SUCCESS
            ? Status.SUCCESS
            : Status.FAILED;

        // emit event
        emit TradeRecovered(tradeOfferHash, trade.status);
    }

    /// @notice Inherit from ICrossChainSwaps
    function withdrawTradeAssets(
        bytes32 tradeOfferHash,
        TradeConstants calldata tradeConstants
    ) external override whenNotPaused nonReentrant {
        // check trade with given offer hash has currect status
        Trade memory trade = _state.trades[tradeOfferHash];
        if (trade.status != Status.SUCCESS && trade.status != Status.FAILED) {
            revert SwapError(SwapErrorCode.INVALID_TRADE_STATUS);
        }

        // check if correct trade constants are passed
        _checkTradeConstants(trade, tradeConstants);

        // send assets to buyer and seller if not already withdrawn
        if (_msgSender() == tradeConstants.buyer) {
            if ((trade.withdrawBitmap & BUYER_BITMASK) > 0) {
                revert SwapError(SwapErrorCode.BUYER_ALREADY_CLAIMED);
            }
            IVault(_state.vaultAddress).sendAssets(
                trade.status == Status.SUCCESS
                    ? tradeConstants.offeringAssets
                    : tradeConstants.considerationAssets,
                tradeConstants.buyer
            );
            _state.trades[tradeOfferHash].withdrawBitmap |= BUYER_BITMASK;
        } else if (_msgSender() == tradeConstants.seller) {
            if ((trade.withdrawBitmap & SELLER_BITMASK) > 0) {
                revert SwapError(SwapErrorCode.SELLER_ALREADY_CLAIMED);
            }
            IVault(_state.vaultAddress).sendAssets(
                trade.status == Status.SUCCESS
                    ? tradeConstants.considerationAssets
                    : tradeConstants.offeringAssets,
                tradeConstants.seller
            );
            _state.trades[tradeOfferHash].withdrawBitmap |= SELLER_BITMASK;
        }

        emit AssetsWithdrew(tradeOfferHash, _msgSender());
    }

    /// @notice Inherit from IWormholeReceiver
    function receiveWormholeMessages(
        bytes memory payload,
        bytes[] memory,
        bytes32 sourceAddress,
        uint16 sourceChain,
        bytes32 deliveryHash
    ) external payable override onlyRelayer {
        // perform sanity checks
        _wormholeMessageValidation(
            address(uint160(uint256(sourceAddress))),
            sourceChain,
            deliveryHash
        );
        // get action type
        PayloadType payloadType = Decoder.getPayloadType(payload);

        if (payloadType == PayloadType.PROCESS_TRADE) {
            // decode trade message
            TradeAcceptMessage memory trade = Decoder.decodeTradeAcceptMessage(
                payload
            );
            (
                bytes memory _payload,
                TradeDetailed memory _finalTrade,
                Status _status,
                bool success
            ) = _processTrade(trade);

            // forward message with successs status
            uint64 sequence = _forwardWormholeMessage(
                _payload,
                sourceChain,
                trade.buyer
            );

            if (success)
                emit TradeProcessed(
                    trade.tradeOfferHash,
                    _finalTrade,
                    _status,
                    sequence
                );
        } else {
            TradeResultMessage memory message = Decoder
                .decodeTradeResultMessage(payload);

            _completeTrade(message);
        }
    }

    /// -----------------------------------------------------------------------
    /// Owner actions
    /// -----------------------------------------------------------------------

    /// @notice Inherit from ICrossChainSwaps
    function setWormholeRelayer(
        address wormholeRelayer_
    ) external override onlyOwner {
        _setWormholeRelayer(wormholeRelayer_);
    }

    /// @notice Inherit from ICrossChainSwaps
    function setConsistencyLevel(
        uint8 consistencyLevel_
    ) external override onlyOwner {
        _setConsistencyLevel(consistencyLevel_);
    }

    /// @notice Inherit from ICrossChainSwaps
    function setWormholeChainId(
        uint16 wormholeChainId_
    ) external override onlyOwner {
        _setWormholeChainId(wormholeChainId_);
    }

    /// @notice Inherit from ICrossChainSwaps
    function addTargetContractAddress(
        address targetContractAddress_,
        uint16 targetChainId_
    ) external override onlyOwner {
        _addTargetContractAddress(targetContractAddress_, targetChainId_);

        emit TargetContractAdded(targetContractAddress_, targetChainId_);
    }

    /// @notice Inherit from ICrossChainSwaps
    function setVault(address vaultAddress_) external override onlyOwner {
        _setVault(vaultAddress_);
    }

    /// @notice Intherit from ICrossChainSwaps
    function setGasSignatureAdmin(
        address gasSignatureAdmin_
    ) external override onlyOwner {
        _setGasSignatureAdmin(gasSignatureAdmin_);
    }

    /// @notice Inherit from ICrossChainSwaps
    function setTokenTypes(
        address[] calldata _tokens,
        AssetType[] calldata _types
    ) external override onlyOwner {
        _setTokenTypes(_tokens, _types);

        emit TokensTypeSet(_tokens, _types);
    }

    /// -----------------------------------------------------------------------
    /// Internal functions
    /// -----------------------------------------------------------------------

    /// @dev helper function to perform sanity checks while initiating a
    ///      cross chain trade
    /// @param _offer Trade offer params
    /// @param _signature signature of the offer parameters
    /// @param _forwardingGas Gas forwarding details struct
    /// @param _gasSignature Signaute of the gas forwarding params
    function _initTradeSanityChecks(
        TradeOffer calldata _offer,
        bytes memory _signature,
        ForwardingGas calldata _forwardingGas,
        bytes memory _gasSignature
    ) private view {
        // verify typed signature
        _verifyTradeOfferSignature(_offer, _signature);
        _verifyForwardingGasSignature(_forwardingGas, _gasSignature);

        // check gas signature owner
        if (_forwardingGas.owner != _state.gasSignatureAdmin) {
            revert SwapError(SwapErrorCode.INVALID_SIGNATURE_ADMIN);
        }

        // nonce validation
        if (getNonce(_offer.owner, _offer.nonce)) {
            revert SwapError(SwapErrorCode.INVALID_OFFER_NONCE);
        }

        if (getGasNonce(_forwardingGas.nonce)) {
            revert SwapError(SwapErrorCode.INVALID_FORWARDER_NONCE);
        }

        // check expiration
        if (_offer.timePeriod < block.timestamp) {
            revert SwapError(SwapErrorCode.OFFER_EXPIRED);
        }

        // check accepted by correct user address
        address intendedFor = _offer.tradeIntendedFor;
        if (!(intendedFor == address(0) || intendedFor == _msgSender())) {
            revert SwapError(SwapErrorCode.INTENDED_FOR_P2P_TRADE);
        }

        // check emitter chainId in signature
        // check if receiver chainId is registred
        if (
            _offer.primaryChainId != _state.wormholeChainId ||
            _state.targetChainIdToContract[uint16(_offer.secondaryChainId)] ==
            address(0)
        ) {
            revert SwapError(SwapErrorCode.INVALID_CHAIN_IDS);
        }

        // get assets type
        bool isOfferValid = verifyAssetWhitelist(
            _offer.offeringAssets.primaryChainAssets
        );

        bool isConsiderationValid = verifyAssetWhitelist(
            _offer.offeringAssets.primaryChainAssets
        );

        if (!(isOfferValid && isConsiderationValid)) {
            revert SwapError(SwapErrorCode.ASSETS_NOT_WHITELISTED);
        }
    }

    /// @dev helper function to perform wormhole specific validation on the
    ///      received message
    /// @param sourceAddress Address of the contract at source chain
    /// @param sourceChain wormhole chainId of the source chain
    /// @param deliveryHash message hash of the wormhole delivery
    function _wormholeMessageValidation(
        address sourceAddress,
        uint16 sourceChain,
        bytes32 deliveryHash
    ) private {
        // check if correct emitter
        uint16 _sourceChainId = _state.targetContractToChainId[sourceAddress];
        address _sourceAddress = _state.targetChainIdToContract[sourceChain];

        if (
            _sourceChainId == 0 ||
            _sourceAddress == address(0) ||
            _sourceAddress != sourceAddress
        ) {
            revert SwapError(SwapErrorCode.INVALID_CHAIN_IDS);
        }

        // completed (replay protection), also serves as reentrancy protection
        if (messageHashConsumed(deliveryHash)) {
            revert SwapError(SwapErrorCode.MESSAGE_ALREADY_CONSUMED);
        }
        _consumeMessageHash(deliveryHash);
    }

    /// @dev helper function to process trade after receving payload as a
    ///      wormhole delivery. Performs sanity checks and pulls assets on this chain
    ///      Emits a new message for wormhole to deliver back to initial chain
    /// @param trade Trade accepting message sent by wormhole
    function _processTrade(
        TradeAcceptMessage memory trade
    ) private returns (bytes memory, TradeDetailed memory, Status, bool) {
        TradeDetailed memory finalTrade;
        // check if correct chain ids are in place
        if (trade.secondaryChainId != _state.wormholeChainId) {
            // return with failure message payload
            return (
                _getForwardingMessagePayload(
                    trade.tradeOfferHash,
                    Result.FAILED,
                    trade.primaryChainId,
                    trade.secondaryChainId
                ),
                finalTrade,
                Status.FAILED,
                false
            );
        }

        // get validate asset types
        bool isOfferValid = verifyAssetWhitelist(trade.offeringAssets);
        bool isConsiderationValid = verifyAssetWhitelist(
            trade.considerationAssets
        );

        if (!(isOfferValid && isConsiderationValid)) {
            return (
                _getForwardingMessagePayload(
                    trade.tradeOfferHash,
                    Result.FAILED,
                    trade.primaryChainId,
                    trade.secondaryChainId
                ),
                finalTrade,
                Status.FAILED,
                false
            );
        }

        finalTrade = TradeDetailed({
            tradeOfferHash: trade.tradeOfferHash,
            offeringAssets: trade.offeringAssets,
            considerationAssets: trade.considerationAssets,
            seller: trade.seller,
            buyer: trade.buyer,
            status: Status.SUCCESS,
            withdrawBitmap: 0,
            recoveryRequested: false
        });

        // pull assets with external call along with a try catch
        try
            IVault(_state.vaultAddress).receiveAssets(
                finalTrade,
                Fees({ to: address(0), token: address(0), amount: 0 }),
                Fees({ to: address(0), token: address(0), amount: 0 })
            )
        {
            _setTradeData(finalTrade);

            return (
                _getForwardingMessagePayload(
                    trade.tradeOfferHash,
                    Result.SUCCESS,
                    trade.primaryChainId,
                    trade.secondaryChainId
                ),
                finalTrade,
                Status.SUCCESS,
                true
            );
        } catch {
            return (
                _getForwardingMessagePayload(
                    trade.tradeOfferHash,
                    Result.FAILED,
                    trade.primaryChainId,
                    trade.secondaryChainId
                ),
                finalTrade,
                Status.FAILED,
                true
            );
        }
    }

    /// @dev helper function to finalize a trade once it has done processing on
    ///      secondary chain
    ///      Updates the trade status based on the incomming wormhole message
    /// @param message Incomming trade message from wormhole
    function _completeTrade(TradeResultMessage memory message) private {
        // check if correct chain ids are in place
        if (message.primaryChainId != _state.wormholeChainId) {
            revert SwapError(SwapErrorCode.INVALID_CHAIN_IDS);
        }
        // if success => allow to withdraw swapped else => allow to withdraw original
        _state.trades[message.tradeOfferHash].status = message.result ==
            Result.SUCCESS
            ? Status.SUCCESS
            : Status.FAILED;

        emit TradeCompleted(
            message.tradeOfferHash,
            message.result == Result.SUCCESS ? Status.SUCCESS : Status.FAILED
        );
    }

    /// @dev internal helper function to verify trade constants with stored values
    /// @param trade Trade stored onchain
    /// @param tradeConstants Trade constants passed in the call
    function _checkTradeConstants(
        Trade memory trade,
        TradeConstants calldata tradeConstants
    ) internal pure {
        if (
            trade.tradeContantsHash !=
            Encoder.hashTradeConstants(
                tradeConstants.offeringAssets,
                tradeConstants.considerationAssets,
                tradeConstants.buyer,
                tradeConstants.seller
            )
        ) {
            revert SwapError(SwapErrorCode.INVALID_TRADE_CONSTANTS);
        }
    }

    /// @dev internal helper function for modifier onlyRelayer
    function _onlyRelayer() internal view {
        if (_msgSender() != _state.wormholeRelayer) {
            revert SwapError(SwapErrorCode.ONLY_RELAYER);
        }
    }

    /// @dev helper function to get encoded bytes of payload to forward
    /// @param _tradeOfferHash hash of the trade offer
    /// @param _result Final result of the trade
    /// @param _primaryChainId wormhole chain id of the primary chain
    /// @param _secondaryChainId wormhole chain id of the secondary chain
    function _getForwardingMessagePayload(
        bytes32 _tradeOfferHash,
        Result _result,
        uint16 _primaryChainId,
        uint16 _secondaryChainId
    ) private pure returns (bytes memory) {
        return
            Encoder.encodeTradeResultMessage(
                TradeResultMessage({
                    payloadType: PayloadType.COMPLETE_TRADE,
                    tradeOfferHash: _tradeOfferHash,
                    result: _result,
                    primaryChainId: _primaryChainId,
                    secondaryChainId: _secondaryChainId
                })
            );
    }

    /// @dev storage gap
    // solhint-disable-next-line
    uint[50] __gap;
}

File 2 of 29 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 3 of 29 : IERC5267Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.0;

interface IERC5267Upgradeable {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

File 4 of 29 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

File 5 of 29 : PausableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 6 of 29 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 7 of 29 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 8 of 29 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 9 of 29 : draft-EIP712Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/draft-EIP712.sol)

pragma solidity ^0.8.0;

// EIP-712 is Final as of 2022-08-11. This file is deprecated.

import "./EIP712Upgradeable.sol";

File 10 of 29 : ECDSAUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../StringsUpgradeable.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSAUpgradeable {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // 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 (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): 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) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

File 11 of 29 : EIP712Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.8;

import "./ECDSAUpgradeable.sol";
import "../../interfaces/IERC5267Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * _Available since v3.4._
 *
 * @custom:storage-size 52
 */
abstract contract EIP712Upgradeable is Initializable, IERC5267Upgradeable {
    bytes32 private constant _TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    /// @custom:oz-renamed-from _HASHED_NAME
    bytes32 private _hashedName;
    /// @custom:oz-renamed-from _HASHED_VERSION
    bytes32 private _hashedVersion;

    string private _name;
    string private _version;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
        __EIP712_init_unchained(name, version);
    }

    function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
        _name = name;
        _version = version;

        // Reset prior values in storage if upgrading
        _hashedName = 0;
        _hashedVersion = 0;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        return _buildDomainSeparator();
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {EIP-5267}.
     *
     * _Available since v4.9._
     */
    function eip712Domain()
        public
        view
        virtual
        override
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
        // and the EIP712 domain is not reliable, as it will be missing name and version.
        require(_hashedName == 0 && _hashedVersion == 0, "EIP712: Uninitialized");

        return (
            hex"0f", // 01111
            _EIP712Name(),
            _EIP712Version(),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    /**
     * @dev The name parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712Name() internal virtual view returns (string memory) {
        return _name;
    }

    /**
     * @dev The version parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712Version() internal virtual view returns (string memory) {
        return _version;
    }

    /**
     * @dev The hash of the name parameter for the EIP712 domain.
     *
     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
     */
    function _EIP712NameHash() internal view returns (bytes32) {
        string memory name = _EIP712Name();
        if (bytes(name).length > 0) {
            return keccak256(bytes(name));
        } else {
            // If the name is empty, the contract may have been upgraded without initializing the new storage.
            // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
            bytes32 hashedName = _hashedName;
            if (hashedName != 0) {
                return hashedName;
            } else {
                return keccak256("");
            }
        }
    }

    /**
     * @dev The hash of the version parameter for the EIP712 domain.
     *
     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
     */
    function _EIP712VersionHash() internal view returns (bytes32) {
        string memory version = _EIP712Version();
        if (bytes(version).length > 0) {
            return keccak256(bytes(version));
        } else {
            // If the version is empty, the contract may have been upgraded without initializing the new storage.
            // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
            bytes32 hashedVersion = _hashedVersion;
            if (hashedVersion != 0) {
                return hashedVersion;
            } else {
                return keccak256("");
            }
        }
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[48] private __gap;
}

File 12 of 29 : MathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 13 of 29 : SignedMathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMathUpgradeable {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 14 of 29 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = MathUpgradeable.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, MathUpgradeable.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed 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";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 15 of 29 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // 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 (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): 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) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

File 16 of 29 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 17 of 29 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 18 of 29 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed 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";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 19 of 29 : BitMaps.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/BitMaps.sol)
pragma solidity ^0.8.0;

/**
 * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
 * Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
 */
library BitMaps {
    struct BitMap {
        mapping(uint256 => uint256) _data;
    }

    /**
     * @dev Returns whether the bit at `index` is set.
     */
    function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        return bitmap._data[bucket] & mask != 0;
    }

    /**
     * @dev Sets the bit at `index` to the boolean `value`.
     */
    function setTo(BitMap storage bitmap, uint256 index, bool value) internal {
        if (value) {
            set(bitmap, index);
        } else {
            unset(bitmap, index);
        }
    }

    /**
     * @dev Sets the bit at `index`.
     */
    function set(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] |= mask;
    }

    /**
     * @dev Unsets the bit at `index`.
     */
    function unset(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] &= ~mask;
    }
}

File 20 of 29 : CrossChainSwapsSignatureUtil.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {
    EIP712Upgradeable
} from "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
import {
    TradeOffer,
    ForwardingGas,
    Fees,
    AssetsSet,
    Assets,
    AssetData
} from "../utils/DataTypes.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

/// @title Cross Chain Swaps Signing Utils
/// @author NF3 Exchange
/// @dev  Helper contract for Protocol. This contract manages verifying signatures
///       from off-chain Protocol orders.

abstract contract CrossChainSwapsSignatureUtil is EIP712Upgradeable {
    /// -----------------------------------------------------------------------
    /// Errors
    /// -----------------------------------------------------------------------

    enum SigningUtilsErrorCodes {
        INVALID_TRADE_OFFER_SIGNATURE,
        INVALID_GAS_FORWARDING_SIGNATURE
    }

    error SigningUtilsError(SigningUtilsErrorCodes code);

    /// -----------------------------------------------------------------------
    /// Library usage
    /// -----------------------------------------------------------------------
    using ECDSA for bytes32;

    /// -----------------------------------------------------------------------
    /// Storage variables
    /// -----------------------------------------------------------------------

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    bytes32 private immutable TRADE_OFFER_TYPE_HASH;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    bytes32 private immutable FORWARDING_GAS_TYPE_HASH;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    bytes32 private immutable FEES_TYPE_HASH;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    bytes32 private immutable ASSETS_SET_TYPE_HASH;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    bytes32 private immutable ASSETS_TYPE_HASH;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    bytes32 private immutable ASSET_DATA_TYPE_HASH;

    /* ===== INIT ===== */

    /// @dev Constructor
    /// @dev Calculate and set type hashes for all the structs and nested structs types
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        bytes memory assetDataTypeString = abi.encodePacked(
            "AssetData(",
            "address token,",
            "uint8 assetType,",
            "uint256 tokenId,",
            "uint256 amount",
            ")"
        );

        bytes memory assetsTypeString = abi.encodePacked(
            "Assets(",
            "AssetData[] assets"
            ")"
        );

        bytes memory assetsSetTypeString = abi.encodePacked(
            "AssetsSet(",
            "Assets primaryChainAssets,",
            "Assets secondaryChainAssets",
            ")"
        );

        bytes memory feesTypeString = abi.encodePacked(
            "Fees(",
            "address to,",
            "address token,",
            "uint256 amount",
            ")"
        );

        bytes memory tradeOfferTypeString = abi.encodePacked(
            "TradeOffer(",
            "AssetsSet offeringAssets,",
            "AssetsSet considerationAssets,",
            "Fees buyerFees,",
            "Fees sellerFees,",
            "address owner,",
            "uint256 timePeriod,",
            "uint256 nonce,",
            "address tradeIntendedFor,",
            "uint256 primaryChainId,",
            "uint256 secondaryChainId"
            ")"
        );

        bytes memory forwardingGasTypeString = abi.encodePacked(
            "ForwardingGas(",
            "uint256 forwardingValue,",
            "uint256 nonce,",
            "address owner",
            ")"
        );

        TRADE_OFFER_TYPE_HASH = keccak256(
            abi.encodePacked(
                tradeOfferTypeString,
                assetDataTypeString,
                assetsTypeString,
                assetsSetTypeString,
                feesTypeString
            )
        );
        FORWARDING_GAS_TYPE_HASH = keccak256(forwardingGasTypeString);
        FEES_TYPE_HASH = keccak256(feesTypeString);
        ASSETS_SET_TYPE_HASH = keccak256(
            abi.encodePacked(
                assetsSetTypeString,
                assetDataTypeString,
                assetsTypeString
            )
        );
        ASSETS_TYPE_HASH = keccak256(
            abi.encodePacked(assetsTypeString, assetDataTypeString)
        );
        ASSET_DATA_TYPE_HASH = keccak256(assetDataTypeString);
    }

    /// -----------------------------------------------------------------------
    /// Signature Verification Functions
    /// -----------------------------------------------------------------------

    /// @dev Check the signature if the trade offer info is valid or not.
    /// @param _offer trade offer info
    /// @param signature offer signature
    function _verifyTradeOfferSignature(
        TradeOffer calldata _offer,
        bytes memory signature
    ) internal view {
        bytes32 tradeOfferHash = keccak256(
            abi.encode(
                TRADE_OFFER_TYPE_HASH,
                _hashAssetsSet(_offer.offeringAssets),
                _hashAssetsSet(_offer.considerationAssets),
                _hashFees(_offer.buyerFees),
                _hashFees(_offer.sellerFees),
                _offer.owner,
                _offer.timePeriod,
                _offer.nonce,
                _offer.tradeIntendedFor,
                _offer.primaryChainId,
                _offer.secondaryChainId
            )
        );

        address signer = _hashTypedDataV4(tradeOfferHash).recover(signature);
        if (_offer.owner != signer) {
            revert SigningUtilsError(
                SigningUtilsErrorCodes.INVALID_TRADE_OFFER_SIGNATURE
            );
        }
    }

    /// @dev Check the signature if the gas forwarding info is valid or not.
    /// @param _forwardingGas forwarding gas info
    /// @param signature forwardingGas signature
    function _verifyForwardingGasSignature(
        ForwardingGas calldata _forwardingGas,
        bytes memory signature
    ) internal view {
        bytes32 forwardingGasHash = keccak256(
            abi.encode(
                FORWARDING_GAS_TYPE_HASH,
                _forwardingGas.forwardingValue,
                _forwardingGas.nonce,
                _forwardingGas.owner
            )
        );

        address signer = _hashTypedDataV4(forwardingGasHash).recover(signature);
        if (_forwardingGas.owner != signer) {
            revert SigningUtilsError(
                SigningUtilsErrorCodes.INVALID_GAS_FORWARDING_SIGNATURE
            );
        }
    }

    /// -----------------------------------------------------------------------
    /// Private functions
    /// -----------------------------------------------------------------------

    /// @dev Get eip 712 compliant hash for Fees struct type
    /// @param _fees Fees struct to be hashed
    function _hashFees(Fees calldata _fees) private view returns (bytes32) {
        bytes32 feesTypeHash = keccak256(
            abi.encode(FEES_TYPE_HASH, _fees.to, _fees.token, _fees.amount)
        );
        return feesTypeHash;
    }

    /// @dev Get eip 712 compliant hash for AssetsSet struct type
    /// @param _assets AssetsSet struct to be hashed
    function _hashAssetsSet(
        AssetsSet calldata _assets
    ) private view returns (bytes32) {
        bytes32 assetsSetTypeHash = keccak256(
            abi.encode(
                ASSETS_SET_TYPE_HASH,
                _hashAssets(_assets.primaryChainAssets),
                _hashAssets(_assets.secondaryChainAssets)
            )
        );

        return assetsSetTypeHash;
    }

    /// @dev Get eip 712 compliant hash for Assets struct type
    /// @param _assets Assetes struct to be hashed
    function _hashAssets(
        Assets calldata _assets
    ) private view returns (bytes32) {
        uint256 assetsCount = _assets.assets.length;
        bytes32[] memory assetsHashes = new bytes32[](assetsCount);

        for (uint i; i < assetsCount; ) {
            assetsHashes[i] = _hashAssetData(_assets.assets[i]);
            unchecked {
                ++i;
            }
        }

        bytes32 assetsTypeHash = keccak256(
            abi.encode(
                ASSETS_TYPE_HASH,
                keccak256(abi.encodePacked(assetsHashes))
            )
        );

        return assetsTypeHash;
    }

    /// @dev Get eip 712 compliant hash for AssetData struct type
    /// @param _asset AssetData struct to be hashed
    function _hashAssetData(
        AssetData calldata _asset
    ) private view returns (bytes32) {
        bytes32 assetDataTypeHash = keccak256(
            abi.encode(
                ASSET_DATA_TYPE_HASH,
                _asset.token,
                _asset.assetType,
                _asset.tokenId,
                _asset.amount
            )
        );

        return assetDataTypeHash;
    }
}

File 21 of 29 : CrossChainSwapsStorage.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol";
import {
    TradeDetailed,
    AssetType,
    Assets,
    Trade,
    State
} from "../utils/DataTypes.sol";
import { ICrossChainSwaps } from "../interfaces/ICrossChainSwaps.sol";
import { Encoder } from "../libraries/Formatters.sol";

/// @title Cross Chain Swaps Storage
/// @author NF3 Exchange
/// @notice This is an abstract contract that inherits from ICrossChainSwaps interface.
/// @dev This contract acts as a storage provider for other contracts in the inheritance tree

abstract contract CrossChainSwapsStorage is ICrossChainSwaps {
    /// -----------------------------------------------------------------------
    /// Library usage
    /// -----------------------------------------------------------------------

    using BitMaps for BitMaps.BitMap;

    /// -----------------------------------------------------------------------
    /// Storage variables
    /// -----------------------------------------------------------------------

    /// @notice Tightly packed storage values required for the system
    State internal _state;

    /// -----------------------------------------------------------------------
    /// Setter functions
    /// -----------------------------------------------------------------------

    /// @dev Set wormhole relayer's address
    /// @param wormholeRelayer_ Address of the new relayer contract
    function _setWormholeRelayer(address wormholeRelayer_) internal {
        if (wormholeRelayer_ == address(0))
            revert SwapError(SwapErrorCode.INVALID_ADDRESS);
        _state.wormholeRelayer = payable(wormholeRelayer_);
    }

    /// @dev Set consistency level on the current chainId.
    /// See https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consist#consistency-levels
    /// @param consistencyLevel_ value of new consistency level
    function _setConsistencyLevel(uint8 consistencyLevel_) internal {
        _state.consistencyLevel = consistencyLevel_;
    }

    /// @dev Set wormhole chain id of the current evm chain
    /// @param wormholeChainId_ new wormhole chain id for the current evm chain
    function _setWormholeChainId(uint16 wormholeChainId_) internal {
        _state.wormholeChainId = wormholeChainId_;
    }

    /// @dev Register new evm chain for cross chain swaps
    /// @param targetContractAddress_ EVM address of the new chain
    /// @param targetChainId_ wormhole chain id of the new chain
    function _addTargetContractAddress(
        address targetContractAddress_,
        uint16 targetChainId_
    ) internal {
        if (targetContractAddress_ == address(0))
            revert SwapError(SwapErrorCode.INVALID_ADDRESS);
        _state.targetContractToChainId[targetContractAddress_] = targetChainId_;
        _state.targetChainIdToContract[targetChainId_] = targetContractAddress_;
    }

    /// @dev Update nonce of a user address
    /// @param owner User's address
    /// @param _nonce Nonce to be updated
    function _setNonce(address owner, uint _nonce) internal {
        _state.nonce[owner].set(_nonce);
    }

    /// @dev Update nonce of gas forwarding message
    /// @param _nonce Nonce to be updated
    function _setGasNonce(uint _nonce) internal {
        _state.gasForwardingNonce.set(_nonce);
    }

    /// @dev Update the incomming trade message as consumed
    /// @param vmHash Hash of the trade offer
    function _consumeMessageHash(bytes32 vmHash) internal {
        _state.consumedMessages[vmHash] = true;
    }

    /// @dev Whitelist new token, allowing trade on the platform
    /// @param _tokens Addresses of new tokens
    /// @param _types Types of asset corrosponding to each token address
    function _setTokenTypes(
        address[] calldata _tokens,
        AssetType[] calldata _types
    ) internal {
        for (uint256 i = 0; i < _tokens.length; i++) {
            _state.types[_tokens[i]] = _types[i];
        }
    }

    /// @dev Update address of vault contract. Restricted function, can only be called by owner
    /// @param vaultAddress_ address of new vault
    function _setVault(address vaultAddress_) internal {
        if (vaultAddress_ == address(0))
            revert SwapError(SwapErrorCode.INVALID_ADDRESS);
        _state.vaultAddress = vaultAddress_;
    }

    /// @dev Update address of the admin who will sign the gas forwarding message
    /// @param gasSignatureAdmin_ address of new gas admin
    function _setGasSignatureAdmin(address gasSignatureAdmin_) internal {
        if (gasSignatureAdmin_ == address(0))
            revert SwapError(SwapErrorCode.INVALID_ADDRESS);
        _state.gasSignatureAdmin = gasSignatureAdmin_;
    }

    /// @dev Set data a trade corrosponding to it's trade offer hash
    /// @param _trade Trade data to be stored
    function _setTradeData(TradeDetailed memory _trade) internal {
        Trade storage trade = _state.trades[_trade.tradeOfferHash];
        trade.tradeContantsHash = Encoder.hashTradeConstants(
            _trade.offeringAssets,
            _trade.considerationAssets,
            _trade.buyer,
            _trade.seller
        );
        trade.status = _trade.status;
        trade.withdrawBitmap = _trade.withdrawBitmap;
    }

    /// -----------------------------------------------------------------------
    /// Getter functions
    /// -----------------------------------------------------------------------

    /// @dev wormhole core contract address
    function wormholeRelayer() public view returns (address) {
        return _state.wormholeRelayer;
    }

    /// @dev number of confirmations for wormhole messages
    function consistencyLevel() public view returns (uint8) {
        return _state.consistencyLevel;
    }

    /// @dev wormhole chain id of current network
    function wormholeChainId() public view returns (uint16) {
        return _state.wormholeChainId;
    }

    /// @dev assets vault for the system
    function vaultAddress() public view returns (address) {
        return _state.vaultAddress;
    }

    /// @dev admin's address who will sign the gas signatures
    function gasSignatureAdmin() public view returns (address) {
        return _state.gasSignatureAdmin;
    }

    /// @dev mapping from target contract on other chain to their wormhole chain id
    /// @param _contract Target contract address
    function targetContractToChainId(
        address _contract
    ) public view returns (uint16) {
        return _state.targetContractToChainId[_contract];
    }

    /// @dev mapping from wormhole chain id to target contract on other chain
    /// @param _chainId Target chainId
    function targetChainIdToContract(
        uint16 _chainId
    ) public view returns (address) {
        return _state.targetChainIdToContract[_chainId];
    }

    /// @dev trade data mapped to it's hash
    /// @param _hash Trade offer hash for the given trade
    function trades(bytes32 _hash) public view returns (Trade memory) {
        return _state.trades[_hash];
    }

    /// @dev Get nonce status of a user and nonce value
    /// @param owner User's address
    /// @param _nonce Nonce value to check
    function getNonce(address owner, uint _nonce) public view returns (bool) {
        return _state.nonce[owner].get(_nonce);
    }

    /// @dev Get nonce status of the gas forwarding admin
    /// @param _nonce Nonce value to check
    function getGasNonce(uint _nonce) public view returns (bool) {
        return _state.gasForwardingNonce.get(_nonce);
    }

    /// @dev Check if message hash is already consumed or not
    /// @param hash Message hash to check
    function messageHashConsumed(bytes32 hash) public view returns (bool) {
        return _state.consumedMessages[hash];
    }

    /// @dev Check if a give set of assets are whitelisted and their token
    ///      types are set correctly or not
    /// @param _assets Assets struct to be checked for whitelist
    function verifyAssetWhitelist(
        Assets memory _assets
    ) public view returns (bool) {
        uint i;
        uint len = _assets.assets.length;

        // loop through assets and check their types
        for (i; i < len; ) {
            if (
                _state.types[_assets.assets[i].token] !=
                _assets.assets[i].assetType
            ) {
                return false;
            }
            unchecked {
                ++i;
            }
        }

        return true;
    }
}

File 22 of 29 : WormholeInteraction.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import { IWormholeRelayer, VaaKey } from "../interfaces/IWormholeRelayer.sol";
import { CrossChainSwapsStorage } from "./CrossChainSwapsStorage.sol";
import {
    Assets,
    AssetType,
    GAS_REQUIRED_FOR_ERC20,
    GAS_REQUIRED_FOR_ERC721,
    GAS_REQUIRED_FOR_ERC1155,
    BASE_GAS_REQUIRED,
    BASE_GAS_FOR_FORWARDING
} from "../utils/DataTypes.sol";

/// @title Wormhole Interactions
/// @author NF3 Exchange
/// @notice This is an abstract contract that inherits from CrossChainSwapsStorage.
/// @dev This contract acts as a layer responsible for sending and forwarding wormhole messages

abstract contract WormholeInteraction is CrossChainSwapsStorage {
    /// -----------------------------------------------------------------------
    /// Wormhole actions
    /// -----------------------------------------------------------------------

    /// @dev Send a wormhole message to the specifed chain id and contract
    ///      address along with the give payload
    /// @param payload Encoded payload bytes to be sent
    /// @param offeringAssets Assets offered on the secondary chain
    /// @param considerationAssets Assets required as consideration on secondary chain
    /// @param _targetAddress Address of cross chain contract on secondary chain
    /// @param _targetChainId ChainId to which message needs to be delivered
    /// @param _refundChainId ChainId on which the remainig gas will be refunded to
    /// @param _receiverValue Amount to be sent to second chain to fund forwarding
    function _sendWormholeMessage(
        bytes memory payload,
        Assets calldata offeringAssets,
        Assets calldata considerationAssets,
        address _targetAddress,
        uint16 _targetChainId,
        uint16 _refundChainId,
        uint256 _receiverValue
    ) internal returns (uint64 sequence) {
        // get estimated gas requried
        uint256 amountOfGasRequried = calculateGasForRelay(
            offeringAssets,
            considerationAssets
        );

        // get quote on deliver cost
        (uint256 cost, ) = IWormholeRelayer(_state.wormholeRelayer)
            .quoteEVMDeliveryPrice(
                _targetChainId,
                _receiverValue,
                amountOfGasRequried
            );

        if (cost > msg.value) {
            revert SwapError(SwapErrorCode.INSUFFICIENT_GAS_FEES_PROVIDED);
        }

        // emit message and request delivery
        sequence = IWormholeRelayer(_state.wormholeRelayer).sendPayloadToEvm{
            value: msg.value
        }(
            _targetChainId,
            _targetAddress,
            payload,
            _receiverValue,
            amountOfGasRequried,
            _refundChainId,
            msg.sender
        );
    }

    /// @dev Forward a wormhole message to the specifed chain id and contract
    ///      address along with the give payload
    /// @param _payload Encoded payload bytes to be sent
    /// @param _targetChainId ChainId to which message needs to be delivered
    /// @param _refundAddress Address to which gas refund should be sent
    function _forwardWormholeMessage(
        bytes memory _payload,
        uint16 _targetChainId,
        address _refundAddress
    ) internal returns (uint64 sequence) {
        uint256 receiverValue = 0;

        sequence = IWormholeRelayer(_state.wormholeRelayer).sendPayloadToEvm{
            value: msg.value
        }(
            _targetChainId,
            _state.targetChainIdToContract[_targetChainId],
            _payload,
            receiverValue,
            BASE_GAS_FOR_FORWARDING,
            _targetChainId,
            _refundAddress
        );
    }

    /// -----------------------------------------------------------------------
    /// internal actions
    /// -----------------------------------------------------------------------

    /// @dev Helper function to calculate gas required for relaying the message
    /// @param offeringAssets Assets offered on the secondary chain
    /// @param considerationAssets Assets required as consideration on secondary chain
    function calculateGasForRelay(
        Assets calldata offeringAssets,
        Assets calldata considerationAssets
    ) public pure returns (uint256) {
        // base amount for relaying
        uint amountOfGasRequired = BASE_GAS_REQUIRED;

        // amount of gas required for pulling assets on the other chain
        amountOfGasRequired +=
            _calculateGasRequriedForPullingAssets(offeringAssets) +
            _calculateGasRequriedForPullingAssets(considerationAssets);

        // adding a buffer of base gas
        amountOfGasRequired += BASE_GAS_FOR_FORWARDING;

        return amountOfGasRequired;
    }

    /// @dev Helper function to calculate gas required for pulling assets
    /// @param _assets Assets to be pulled on the other chain
    function _calculateGasRequriedForPullingAssets(
        Assets calldata _assets
    ) private pure returns (uint256) {
        uint gasRequired = 0;

        uint len = _assets.assets.length;
        uint i;
        for (i; i < len; ) {
            if (_assets.assets[i].assetType == AssetType.ERC_721)
                gasRequired += GAS_REQUIRED_FOR_ERC721;
            else if (_assets.assets[i].assetType == AssetType.ERC_20)
                gasRequired += GAS_REQUIRED_FOR_ERC20;
            else gasRequired += GAS_REQUIRED_FOR_ERC1155;
            unchecked {
                ++i;
            }
        }

        return gasRequired;
    }
}

File 23 of 29 : ICrossChainSwaps.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {
    TradeOffer,
    AssetType,
    Result,
    Status,
    ForwardingGas,
    TradeConstants,
    TradeDetailed
} from "../utils/DataTypes.sol";

/// @title Cross chain swaps interface
/// @author NF3 Exchange
/// @dev This interface defines all the functions related initiating and finalizing cross chain trades.

interface ICrossChainSwaps {
    /// -----------------------------------------------------------------------
    /// Errors
    /// -----------------------------------------------------------------------

    enum SwapErrorCode {
        INVALID_ADDRESS,
        INVALID_OFFER_NONCE,
        INVALID_FORWARDER_NONCE,
        OFFER_EXPIRED,
        INTENDED_FOR_P2P_TRADE,
        INVALID_CHAIN_IDS,
        ASSETS_NOT_WHITELISTED,
        MESSAGE_ALREADY_CONSUMED,
        ONLY_RELAYER,
        INVALID_TRADE_STATUS,
        BUYER_ALREADY_CLAIMED,
        SELLER_ALREADY_CLAIMED,
        TRADE_OWNERS_ONLY,
        INVALID_INPUT,
        RECOVERY_NOT_REQUESTED,
        INSUFFICIENT_GAS_FEES_PROVIDED,
        INVALID_SIGNATURE_ADMIN,
        INVALID_TRADE_CONSTANTS,
        OFFER_OWNER_ONLY
    }

    error SwapError(SwapErrorCode code);

    /// -----------------------------------------------------------------------
    /// Events
    /// -----------------------------------------------------------------------

    /// @dev Emits when a new trade is initialzied
    /// @param offer Trade offer struct
    /// @param tradeOfferHash Hash of the trade from which assetes are withdrawn
    /// @param trade Details of the trade data on the current chain
    /// @param buyer Buyer's address
    /// @param seller Seller's address
    /// @param wormholeSequence Wormhole's message sequence number
    event TradeInitialized(
        TradeOffer offer,
        bytes32 indexed tradeOfferHash,
        TradeDetailed trade,
        address indexed buyer,
        address indexed seller,
        uint256 wormholeSequence
    );

    /// @dev Emits when a trade offer is cancelled
    /// @param offer Trade offer struct
    event TradeOfferCancelled(TradeOffer offer);

    /// @dev Emits when trade assets are withdrawn by the users
    /// @param tradeOfferHash Hash of the trade from which assetes are withdrawn
    /// @param user User address which has withdrawn the assets
    event AssetsWithdrew(bytes32 indexed tradeOfferHash, address indexed user);

    /// @dev Emits when trade is processed on being the secondary chain
    /// @param tradeOfferHash Hash of the trade offer which has been processed
    /// @param trade Details of the trade data on the current chain
    /// @param status Status to which the trade has moved
    /// @param wormholeSequence Wormhole's message sequence number
    event TradeProcessed(
        bytes32 indexed tradeOfferHash,
        TradeDetailed trade,
        Status status,
        uint64 wormholeSequence
    );

    /// @dev Emits when trade is completed
    /// @param tradeOfferHash Hash of the trade offer which has completed
    /// @param status Status to which the trade has moved
    event TradeCompleted(bytes32 indexed tradeOfferHash, Status status);

    /// @dev Emits when trade recovery is requested by either seler or buyer
    /// @param tradeOfferHash Hash of the stored trade offer who's recovery
    ///        is requested
    event RecoveryRequested(bytes32 indexed tradeOfferHash);

    /// @dev Emits when trade recovery is completed
    /// @param tradeOfferHash Hash of the stored trade offer which has
    ///        been recovered
    /// @param status Status to which trade has moved to (SUCCESS/FAILED)
    event TradeRecovered(bytes32 indexed tradeOfferHash, Status status);

    /// @dev Emits when tokens types are whitelisted by setting tokenType
    /// @param tokens Tokens that are being whitelisted
    /// @param types Token types
    event TokensTypeSet(address[] tokens, AssetType[] types);

    /// @dev Emits when new target chains are added for cross chain activity
    /// @param targetContract EVM address of the new chain
    /// @param targetChainId wormhole chain id of the new chain
    event TargetContractAdded(address targetContract, uint16 targetChainId);

    /// -----------------------------------------------------------------------
    /// Trade actions
    /// -----------------------------------------------------------------------

    /// @dev Cancel an existing trade offer by setting the nonce of that offer
    ///      Can only be called by the owner of the offer
    /// @param offer Trade offer to cancel
    /// @param signature Signature of the trade offer
    function cancelTrade(
        TradeOffer calldata offer,
        bytes memory signature
    ) external;

    /// @dev Initialize trade by lockings assets on chain A and sending a wormhole
    ///      message to chain B
    /// @param offer Details of the trade offer
    /// @param signature seller's signature of the offer parameters
    /// @param forwardingGas Amount to be sent to second chain to fund forwarding
    /// @param gasSignature Admin's signature of forwarding gas message
    /// @return sequence Wormhole sequence number
    function initializeTrade(
        TradeOffer calldata offer,
        bytes memory signature,
        ForwardingGas calldata forwardingGas,
        bytes memory gasSignature
    ) external payable returns (uint64 sequence);

    /// @dev An emergency fallback function in case of wormhole message is not
    ///      able to be delivered, the user assets don't get stuck.
    ///      Either the seller or buyer can request the recovery of a trade and
    ///      furhter processing will be done by the NF3 Admin
    /// @param tradeOfferHash Hash of the trade offer who's recovery is requested
    /// @param tradeConstants Constants involved in this trade requried for matching stored hash
    function requestRecovery(
        bytes32 tradeOfferHash,
        TradeConstants calldata tradeConstants
    ) external;

    /// @dev The trade can be set to FAILED or SUCCESS state based
    ///      on the activity on other chain To update the trade status
    ///      Restricted function, can only be called by the owner when a
    ///      recovery is requested by the seller of buyer
    /// @param tradeOfferHash Hash of the trade offer who's recovery is set
    /// @param result Result to which trade will be updated to
    function recoverTrade(bytes32 tradeOfferHash, Result result) external;

    /// @dev Withdraw assets after trade is completed. If trade was SUCCESS then
    ///      users withdraw swapped assets else withdraw their original assets
    /// @param tradeOfferHash Hash of the original trade offer
    /// @param tradeConstants Constants involved in this trade requried for matching stored hash
    function withdrawTradeAssets(
        bytes32 tradeOfferHash,
        TradeConstants calldata tradeConstants
    ) external;

    /// -----------------------------------------------------------------------
    /// Owner actions
    /// -----------------------------------------------------------------------

    /// @dev Set wormhole relayer's address. Restricted function, can only be called by owner
    /// @param wormholeRelayer Address of the new relayer contract
    function setWormholeRelayer(address wormholeRelayer) external;

    /// @dev Set consistency level on the current chainId. Restricted function, can only be called by owner
    /// See https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consist#consistency-levels
    /// @param consistencyLevel value of new consistency level
    function setConsistencyLevel(uint8 consistencyLevel) external;

    /// @dev Set wormhole chain id of the current evm chain. Restricted function, can only be called by owner
    /// @param wormholeChainId new wormhole chain id for the current evm chain
    function setWormholeChainId(uint16 wormholeChainId) external;

    /// @dev Register new evm chain for cross chain swaps. Restricted function, can only be called by owner
    /// @param targetContractAddress EVM address of the new chain
    /// @param targetChainId wormhole chain id of the new chain
    function addTargetContractAddress(
        address targetContractAddress,
        uint16 targetChainId
    ) external;

    /// @dev Update address of vault contract. Restricted function, can only be called by owner
    /// @param vaultAddress address of new vault
    function setVault(address vaultAddress) external;

    /// @dev Update address of the admin who will sign the gas forwarding
    ///      message. Restricted function, can only be called by owner
    /// @param gasSignatureAdmin address of new gas admin
    function setGasSignatureAdmin(address gasSignatureAdmin) external;

    /// @dev Whitelist new token, allowing trade on the platform.  Restricted function, can only be called by owner
    /// @param tokens Addresses of new tokens
    /// @param types Types of asset corrosponding to each token address
    function setTokenTypes(
        address[] calldata tokens,
        AssetType[] calldata types
    ) external;
}

File 24 of 29 : IVault.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import { Assets, Fees, TradeDetailed } from "../utils/DataTypes.sol";

/// @title NF3 Vault Interface
/// @author NF3 Exchange
/// @dev This interface defines all the functions related to assets transfer and assets escrow.

interface IVault {
    /// -----------------------------------------------------------------------
    /// Errors
    /// -----------------------------------------------------------------------

    enum VaultErrorCodes {
        CALLER_NOT_ALLOWED,
        INVALID_ADDRESS
    }

    error VaultError(VaultErrorCodes code);

    /// -----------------------------------------------------------------------
    /// Events
    /// -----------------------------------------------------------------------

    /// @dev Emits when assets are transffered from a user to the vault
    /// @param assets Assets struct that is transfered
    /// @param from Address from which assets are transfered
    event AssetsReceived(Assets assets, address indexed from);

    /// @dev Emits when assets are transffered to a user from the vault
    /// @param assets Assets struct that is transfered
    /// @param to Address to which assets are transfered
    event AssetsSent(Assets assets, address indexed to);

    /// @dev Emits when fees is transferred from buyer and seller
    /// @param fees Fees details that are taken from buyer and seller
    /// @param user Address of the user who paid the fee
    event FeeTransferred(Fees fees, address indexed user);

    /// @dev Emits when new core contract address has set.
    /// @param oldCoreAddress Previous core contract address
    /// @param newCoreAddress New core contract address
    event CoreSet(address oldCoreAddress, address newCoreAddress);

    /// -----------------------------------------------------------------------
    /// Transfer actions
    /// -----------------------------------------------------------------------

    /// @dev Receive assets from seller and buyer, also deducts fees while receiving assets
    ///      Restricted function can only be called by the core contract
    /// @param trade Trade details from which assets need to be received
    /// @param buyerFees Fees details for buyer
    /// @param sellerFees Fees details for seller
    function receiveAssets(
        TradeDetailed calldata trade,
        Fees calldata buyerFees,
        Fees calldata sellerFees
    ) external returns (bool);

    /// @dev Sends locked assets to the mentioned address. Restricted function, can only be called
    ///      by the core contract
    /// @param assets Assets details that need to be sent
    /// @param to Address to which assets need to be sent
    function sendAssets(
        Assets memory assets,
        address to
    ) external returns (bool);

    /// -----------------------------------------------------------------------
    /// Owner actions
    /// -----------------------------------------------------------------------

    /// @dev Set core contract's address. Restricted function, can only be called by owner
    /// @param coreAddress Address of the new core contract
    function setCoreAddress(address coreAddress) external;
}

File 25 of 29 : IWormholeReceiver.sol
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

/**
 * @notice Interface for a contract which can receive Wormhole messages.
 */
interface IWormholeReceiver {
    /**
     * @notice When a `send` is performed with this contract as the target, this function will be
     *     invoked by the WormholeRelayer contract
     *
     * NOTE: This function should be restricted such that only the Wormhole Relayer contract can call it.
     *
     * We also recommend that this function:
     *   - Stores all received `deliveryHash`s in a mapping `(bytes32 => bool)`, and
     *       on every call, checks that deliveryHash has not already been stored in the
     *       map (This is to prevent other users maliciously trying to relay the same message)
     *   - Checks that `sourceChain` and `sourceAddress` are indeed who
     *       you expect to have requested the calling of `send` or `forward` on the source chain
     *
     * The invocation of this function corresponding to the `send` request will have msg.value equal
     *   to the receiverValue specified in the send request.
     *
     * If the invocation of this function reverts or exceeds the gas limit
     *   specified by the send requester, this delivery will result in a `ReceiverFailure`.
     *
     * @param payload - an arbitrary message which was included in the delivery by the
     *     requester.
     * @param additionalVaas - Additional VAAs which were requested to be included in this delivery.
     *   They are guaranteed to all be included and in the same order as was specified in the
     *     delivery request.
     * @param sourceAddress - the (wormhole format) address on the sending chain which requested
     *     this delivery.
     * @param sourceChain - the wormhole chain ID where this delivery was requested.
     * @param deliveryHash - the VAA hash of the deliveryVAA.
     *
     * NOTE: These signedVaas are NOT verified by the Wormhole core contract prior to being provided
     *     to this call. Always make sure `parseAndVerify()` is called on the Wormhole core contract
     *     before trusting the content of a raw VAA, otherwise the VAA may be invalid or malicious.
     */
    function receiveWormholeMessages(
        bytes memory payload,
        bytes[] memory additionalVaas,
        bytes32 sourceAddress,
        uint16 sourceChain,
        bytes32 deliveryHash
    ) external payable;
}

File 26 of 29 : IWormholeRelayer.sol
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

/**
 * @title WormholeRelayer
 * @author
 * @notice This project allows developers to build cross-chain applications powered by Wormhole without needing to
 * write and run their own relaying infrastructure
 *
 * We implement the IWormholeRelayer interface that allows users to request a delivery provider to relay a payload (and/or additional VAAs)
 * to a chain and address of their choice.
 */

/**
 * @notice VaaKey identifies a wormhole message
 *
 * @custom:member chainId Wormhole chain ID of the chain where this VAA was emitted from
 * @custom:member emitterAddress Address of the emitter of the VAA, in Wormhole bytes32 format
 * @custom:member sequence Sequence number of the VAA
 */
struct VaaKey {
    uint16 chainId;
    bytes32 emitterAddress;
    uint64 sequence;
}

interface IWormholeRelayerBase {
    event SendEvent(
        uint64 indexed sequence,
        uint256 deliveryQuote,
        uint256 paymentForExtraReceiverValue
    );

    function getRegisteredWormholeRelayerContract(
        uint16 chainId
    ) external view returns (bytes32);
}

/**
 * @title IWormholeRelayerSend
 * @notice The interface to request deliveries
 */
interface IWormholeRelayerSend is IWormholeRelayerBase {
    /**
     * @notice Publishes an instruction for the default delivery provider
     * to relay a payload to the address `targetAddress` on chain `targetChain`
     * with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
     *
     * `targetAddress` must implement the IWormholeReceiver interface
     *
     * This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
     *
     * Any refunds (from leftover gas) will be paid to the delivery provider. In order to receive the refunds, use the `sendPayloadToEvm` function
     * with `refundChain` and `refundAddress` as parameters
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param gasLimit gas limit with which to call `targetAddress`.
     * @return sequence sequence number of published VAA containing delivery instructions
     */
    function sendPayloadToEvm(
        uint16 targetChain,
        address targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 gasLimit
    ) external payable returns (uint64 sequence);

    /**
     * @notice Publishes an instruction for the default delivery provider
     * to relay a payload to the address `targetAddress` on chain `targetChain`
     * with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
     *
     * Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
     * `targetAddress` must implement the IWormholeReceiver interface
     *
     * This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
     *        `targetChainRefundPerGasUnused` rate quoted by the delivery provider
     * @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
     * @param refundAddress The address on `refundChain` to deliver any refund to
     * @return sequence sequence number of published VAA containing delivery instructions
     */
    function sendPayloadToEvm(
        uint16 targetChain,
        address targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 gasLimit,
        uint16 refundChain,
        address refundAddress
    ) external payable returns (uint64 sequence);

    /**
     * @notice Publishes an instruction for the default delivery provider
     * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
     * with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
     *
     * `targetAddress` must implement the IWormholeReceiver interface
     *
     * This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
     *
     * Any refunds (from leftover gas) will be paid to the delivery provider. In order to receive the refunds, use the `sendVaasToEvm` function
     * with `refundChain` and `refundAddress` as parameters
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param gasLimit gas limit with which to call `targetAddress`.
     * @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
     * @return sequence sequence number of published VAA containing delivery instructions
     */
    function sendVaasToEvm(
        uint16 targetChain,
        address targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 gasLimit,
        VaaKey[] memory vaaKeys
    ) external payable returns (uint64 sequence);

    /**
     * @notice Publishes an instruction for the default delivery provider
     * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
     * with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
     *
     * Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
     * `targetAddress` must implement the IWormholeReceiver interface
     *
     * This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
     *        `targetChainRefundPerGasUnused` rate quoted by the delivery provider
     * @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
     * @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
     * @param refundAddress The address on `refundChain` to deliver any refund to
     * @return sequence sequence number of published VAA containing delivery instructions
     */
    function sendVaasToEvm(
        uint16 targetChain,
        address targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 gasLimit,
        VaaKey[] memory vaaKeys,
        uint16 refundChain,
        address refundAddress
    ) external payable returns (uint64 sequence);

    /**
     * @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
     * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
     * with gas limit `gasLimit` and `msg.value` equal to
     * receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
     *
     * Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
     * `targetAddress` must implement the IWormholeReceiver interface
     *
     * This function must be called with `msg.value` equal to
     * quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit, deliveryProviderAddress) + paymentForExtraReceiverValue
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
     *        (in addition to the `receiverValue` specified)
     * @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
     *        `targetChainRefundPerGasUnused` rate quoted by the delivery provider
     * @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
     * @param refundAddress The address on `refundChain` to deliver any refund to
     * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
     * @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
     * @param consistencyLevel Consistency level with which to publish the delivery instructions - see
     *        https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
     * @return sequence sequence number of published VAA containing delivery instructions
     */
    function sendToEvm(
        uint16 targetChain,
        address targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 paymentForExtraReceiverValue,
        uint256 gasLimit,
        uint16 refundChain,
        address refundAddress,
        address deliveryProviderAddress,
        VaaKey[] memory vaaKeys,
        uint8 consistencyLevel
    ) external payable returns (uint64 sequence);

    /**
     * @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
     * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
     * with `msg.value` equal to
     * receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
     *
     * Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
     * `targetAddress` must implement the IWormholeReceiver interface
     *
     * This function must be called with `msg.value` equal to
     * quoteDeliveryPrice(targetChain, receiverValue, encodedExecutionParameters, deliveryProviderAddress) + paymentForExtraReceiverValue
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
     *        (in addition to the `receiverValue` specified)
     * @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing
     *        e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
     * @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
     * @param refundAddress The address on `refundChain` to deliver any refund to, in Wormhole bytes32 format
     * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
     * @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
     * @param consistencyLevel Consistency level with which to publish the delivery instructions - see
     *        https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
     * @return sequence sequence number of published VAA containing delivery instructions
     */
    function send(
        uint16 targetChain,
        bytes32 targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 paymentForExtraReceiverValue,
        bytes memory encodedExecutionParameters,
        uint16 refundChain,
        bytes32 refundAddress,
        address deliveryProviderAddress,
        VaaKey[] memory vaaKeys,
        uint8 consistencyLevel
    ) external payable returns (uint64 sequence);

    /**
     * @notice Performs the same function as a `send`, except:
     * 1)  Can only be used during a delivery (i.e. in execution of `receiveWormholeMessages`)
     * 2)  Is paid for (along with any other calls to forward) by (any msg.value passed in) + (refund leftover from current delivery)
     * 3)  Only executes after `receiveWormholeMessages` is completed (and thus does not return a sequence number)
     *
     * The refund from the delivery currently in progress will not be sent to the user; it will instead
     * be paid to the delivery provider to perform the instruction specified here
     *
     * Publishes an instruction for the same delivery provider (or default, if the same one doesn't support the new target chain)
     * to relay a payload to the address `targetAddress` on chain `targetChain`
     * with gas limit `gasLimit` and with `msg.value` equal to `receiverValue`
     *
     * The following equation must be satisfied (sum_f indicates summing over all forwards requested in `receiveWormholeMessages`):
     * (refund amount from current execution of receiveWormholeMessages) + sum_f [msg.value_f]
     * >= sum_f [quoteEVMDeliveryPrice(targetChain_f, receiverValue_f, gasLimit_f)]
     *
     * The difference between the two sides of the above inequality will be added to `paymentForExtraReceiverValue` of the first forward requested
     *
     * Any refunds (from leftover gas) from this forward will be paid to the same refundChain and refundAddress specified for the current delivery.
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param gasLimit gas limit with which to call `targetAddress`.
     */
    function forwardPayloadToEvm(
        uint16 targetChain,
        address targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 gasLimit
    ) external payable;

    /**
     * @notice Performs the same function as a `send`, except:
     * 1)  Can only be used during a delivery (i.e. in execution of `receiveWormholeMessages`)
     * 2)  Is paid for (along with any other calls to forward) by (any msg.value passed in) + (refund leftover from current delivery)
     * 3)  Only executes after `receiveWormholeMessages` is completed (and thus does not return a sequence number)
     *
     * The refund from the delivery currently in progress will not be sent to the user; it will instead
     * be paid to the delivery provider to perform the instruction specified here
     *
     * Publishes an instruction for the same delivery provider (or default, if the same one doesn't support the new target chain)
     * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
     * with gas limit `gasLimit` and with `msg.value` equal to `receiverValue`
     *
     * The following equation must be satisfied (sum_f indicates summing over all forwards requested in `receiveWormholeMessages`):
     * (refund amount from current execution of receiveWormholeMessages) + sum_f [msg.value_f]
     * >= sum_f [quoteEVMDeliveryPrice(targetChain_f, receiverValue_f, gasLimit_f)]
     *
     * The difference between the two sides of the above inequality will be added to `paymentForExtraReceiverValue` of the first forward requested
     *
     * Any refunds (from leftover gas) from this forward will be paid to the same refundChain and refundAddress specified for the current delivery.
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param gasLimit gas limit with which to call `targetAddress`.
     * @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
     */
    function forwardVaasToEvm(
        uint16 targetChain,
        address targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 gasLimit,
        VaaKey[] memory vaaKeys
    ) external payable;

    /**
     * @notice Performs the same function as a `send`, except:
     * 1)  Can only be used during a delivery (i.e. in execution of `receiveWormholeMessages`)
     * 2)  Is paid for (along with any other calls to forward) by (any msg.value passed in) + (refund leftover from current delivery)
     * 3)  Only executes after `receiveWormholeMessages` is completed (and thus does not return a sequence number)
     *
     * The refund from the delivery currently in progress will not be sent to the user; it will instead
     * be paid to the delivery provider to perform the instruction specified here
     *
     * Publishes an instruction for the delivery provider at `deliveryProviderAddress`
     * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
     * with gas limit `gasLimit` and with `msg.value` equal to
     * receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
     *
     * Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
     * `targetAddress` must implement the IWormholeReceiver interface
     *
     * The following equation must be satisfied (sum_f indicates summing over all forwards requested in `receiveWormholeMessages`):
     * (refund amount from current execution of receiveWormholeMessages) + sum_f [msg.value_f]
     * >= sum_f [quoteEVMDeliveryPrice(targetChain_f, receiverValue_f, gasLimit_f, deliveryProviderAddress_f) + paymentForExtraReceiverValue_f]
     *
     * The difference between the two sides of the above inequality will be added to `paymentForExtraReceiverValue` of the first forward requested
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
     *        (in addition to the `receiverValue` specified)
     * @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
     *        `targetChainRefundPerGasUnused` rate quoted by the delivery provider
     * @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
     * @param refundAddress The address on `refundChain` to deliver any refund to, in Wormhole bytes32 format
     * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
     * @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
     * @param consistencyLevel Consistency level with which to publish the delivery instructions - see
     *        https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
     */
    function forwardToEvm(
        uint16 targetChain,
        address targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 paymentForExtraReceiverValue,
        uint256 gasLimit,
        uint16 refundChain,
        address refundAddress,
        address deliveryProviderAddress,
        VaaKey[] memory vaaKeys,
        uint8 consistencyLevel
    ) external payable;

    /**
     * @notice Performs the same function as a `send`, except:
     * 1)  Can only be used during a delivery (i.e. in execution of `receiveWormholeMessages`)
     * 2)  Is paid for (along with any other calls to forward) by (any msg.value passed in) + (refund leftover from current delivery)
     * 3)  Only executes after `receiveWormholeMessages` is completed (and thus does not return a sequence number)
     *
     * The refund from the delivery currently in progress will not be sent to the user; it will instead
     * be paid to the delivery provider to perform the instruction specified here
     *
     * Publishes an instruction for the delivery provider at `deliveryProviderAddress`
     * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
     * with `msg.value` equal to
     * receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
     *
     * Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
     * `targetAddress` must implement the IWormholeReceiver interface
     *
     * The following equation must be satisfied (sum_f indicates summing over all forwards requested in `receiveWormholeMessages`):
     * (refund amount from current execution of receiveWormholeMessages) + sum_f [msg.value_f]
     * >= sum_f [quoteDeliveryPrice(targetChain_f, receiverValue_f, encodedExecutionParameters_f, deliveryProviderAddress_f) + paymentForExtraReceiverValue_f]
     *
     * The difference between the two sides of the above inequality will be added to `paymentForExtraReceiverValue` of the first forward requested
     *
     * @param targetChain in Wormhole Chain ID format
     * @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format
     * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
     *        (in addition to the `receiverValue` specified)
     * @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing
     *        e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
     * @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
     * @param refundAddress The address on `refundChain` to deliver any refund to, in Wormhole bytes32 format
     * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
     * @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
     * @param consistencyLevel Consistency level with which to publish the delivery instructions - see
     *        https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
     */
    function forward(
        uint16 targetChain,
        bytes32 targetAddress,
        bytes memory payload,
        uint256 receiverValue,
        uint256 paymentForExtraReceiverValue,
        bytes memory encodedExecutionParameters,
        uint16 refundChain,
        bytes32 refundAddress,
        address deliveryProviderAddress,
        VaaKey[] memory vaaKeys,
        uint8 consistencyLevel
    ) external payable;

    /**
     * @notice Requests a previously published delivery instruction to be redelivered
     * (e.g. with a different delivery provider)
     *
     * This function must be called with `msg.value` equal to
     * quoteEVMDeliveryPrice(targetChain, newReceiverValue, newGasLimit, newDeliveryProviderAddress)
     *
     *  @notice *** This will only be able to succeed if the following is true **
     *         - newGasLimit >= gas limit of the old instruction
     *         - newReceiverValue >= receiver value of the old instruction
     *         - newDeliveryProvider's `targetChainRefundPerGasUnused` >= old relay provider's `targetChainRefundPerGasUnused`
     *
     * @param deliveryVaaKey VaaKey identifying the wormhole message containing the
     *        previously published delivery instructions
     * @param targetChain The target chain that the original delivery targeted. Must match targetChain from original delivery instructions
     * @param newReceiverValue new msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param newGasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
     *        `targetChainRefundPerGasUnused` rate quoted by the delivery provider, to the refund chain and address specified in the original request
     * @param newDeliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
     * @return sequence sequence number of published VAA containing redelivery instructions
     *
     * @notice *** This will only be able to succeed if the following is true **
     *         - newGasLimit >= gas limit of the old instruction
     *         - newReceiverValue >= receiver value of the old instruction
     *         - newDeliveryProvider's `targetChainRefundPerGasUnused` >= old relay provider's `targetChainRefundPerGasUnused`
     */
    function resendToEvm(
        VaaKey memory deliveryVaaKey,
        uint16 targetChain,
        uint256 newReceiverValue,
        uint256 newGasLimit,
        address newDeliveryProviderAddress
    ) external payable returns (uint64 sequence);

    /**
     * @notice Requests a previously published delivery instruction to be redelivered
     *
     *
     * This function must be called with `msg.value` equal to
     * quoteDeliveryPrice(targetChain, newReceiverValue, newEncodedExecutionParameters, newDeliveryProviderAddress)
     *
     * @param deliveryVaaKey VaaKey identifying the wormhole message containing the
     *        previously published delivery instructions
     * @param targetChain The target chain that the original delivery targeted. Must match targetChain from original delivery instructions
     * @param newReceiverValue new msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param newEncodedExecutionParameters new encoded information on how to execute delivery that may impact pricing
     *        e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
     * @param newDeliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
     * @return sequence sequence number of published VAA containing redelivery instructions
     *
     *  @notice *** This will only be able to succeed if the following is true **
     *         - (For EVM_V1) newGasLimit >= gas limit of the old instruction
     *         - newReceiverValue >= receiver value of the old instruction
     *         - (For EVM_V1) newDeliveryProvider's `targetChainRefundPerGasUnused` >= old relay provider's `targetChainRefundPerGasUnused`
     */
    function resend(
        VaaKey memory deliveryVaaKey,
        uint16 targetChain,
        uint256 newReceiverValue,
        bytes memory newEncodedExecutionParameters,
        address newDeliveryProviderAddress
    ) external payable returns (uint64 sequence);

    /**
     * @notice Returns the price to request a relay to chain `targetChain`, using the default delivery provider
     *
     * @param targetChain in Wormhole Chain ID format
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param gasLimit gas limit with which to call `targetAddress`.
     * @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
     * @return targetChainRefundPerGasUnused amount of target chain currency that will be refunded per unit of gas unused,
     *         if a refundAddress is specified
     */
    function quoteEVMDeliveryPrice(
        uint16 targetChain,
        uint256 receiverValue,
        uint256 gasLimit
    )
        external
        view
        returns (
            uint256 nativePriceQuote,
            uint256 targetChainRefundPerGasUnused
        );

    /**
     * @notice Returns the price to request a relay to chain `targetChain`, using delivery provider `deliveryProviderAddress`
     *
     * @param targetChain in Wormhole Chain ID format
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param gasLimit gas limit with which to call `targetAddress`.
     * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
     * @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
     * @return targetChainRefundPerGasUnused amount of target chain currency that will be refunded per unit of gas unused,
     *         if a refundAddress is specified
     */
    function quoteEVMDeliveryPrice(
        uint16 targetChain,
        uint256 receiverValue,
        uint256 gasLimit,
        address deliveryProviderAddress
    )
        external
        view
        returns (
            uint256 nativePriceQuote,
            uint256 targetChainRefundPerGasUnused
        );

    /**
     * @notice Returns the price to request a relay to chain `targetChain`, using delivery provider `deliveryProviderAddress`
     *
     * @param targetChain in Wormhole Chain ID format
     * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
     * @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing
     *        e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
     * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
     * @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
     * @return encodedExecutionInfo encoded information on how the delivery will be executed
     *        e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` and `targetChainRefundPerGasUnused`
     *             (which is the amount of target chain currency that will be refunded per unit of gas unused,
     *              if a refundAddress is specified)
     */
    function quoteDeliveryPrice(
        uint16 targetChain,
        uint256 receiverValue,
        bytes memory encodedExecutionParameters,
        address deliveryProviderAddress
    )
        external
        view
        returns (uint256 nativePriceQuote, bytes memory encodedExecutionInfo);

    /**
     * @notice Returns the (extra) amount of target chain currency that `targetAddress`
     * will be called with, if the `paymentForExtraReceiverValue` field is set to `currentChainAmount`
     *
     * @param targetChain in Wormhole Chain ID format
     * @param currentChainAmount The value that `paymentForExtraReceiverValue` will be set to
     * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
     * @return targetChainAmount The amount such that if `targetAddress` will be called with `msg.value` equal to
     *         receiverValue + targetChainAmount
     */
    function quoteNativeForChain(
        uint16 targetChain,
        uint256 currentChainAmount,
        address deliveryProviderAddress
    ) external view returns (uint256 targetChainAmount);

    /**
     * @notice Returns the address of the current default delivery provider
     * @return deliveryProvider The address of (the default delivery provider)'s contract on this source
     *   chain. This must be a contract that implements IDeliveryProvider.
     */
    function getDefaultDeliveryProvider()
        external
        view
        returns (address deliveryProvider);
}

/**
 * @title IWormholeRelayerDelivery
 * @notice The interface to execute deliveries. Only relevant for Delivery Providers
 */
interface IWormholeRelayerDelivery is IWormholeRelayerBase {
    enum DeliveryStatus {
        SUCCESS,
        RECEIVER_FAILURE,
        FORWARD_REQUEST_FAILURE,
        FORWARD_REQUEST_SUCCESS
    }

    enum RefundStatus {
        REFUND_SENT,
        REFUND_FAIL,
        CROSS_CHAIN_REFUND_SENT,
        CROSS_CHAIN_REFUND_FAIL_PROVIDER_NOT_SUPPORTED,
        CROSS_CHAIN_REFUND_FAIL_NOT_ENOUGH
    }

    /**
     * @custom:member recipientContract - The target contract address
     * @custom:member sourceChain - The chain which this delivery was requested from (in wormhole
     *     ChainID format)
     * @custom:member sequence - The wormhole sequence number of the delivery VAA on the source chain
     *     corresponding to this delivery request
     * @custom:member deliveryVaaHash - The hash of the delivery VAA corresponding to this delivery
     *     request
     * @custom:member gasUsed - The amount of gas that was used to call your target contract
     * @custom:member status:
     *   - RECEIVER_FAILURE, if the target contract reverts
     *   - SUCCESS, if the target contract doesn't revert and no forwards were requested
     *   - FORWARD_REQUEST_FAILURE, if the target contract doesn't revert, forwards were requested,
     *       but provided/leftover funds were not sufficient to cover them all
     *   - FORWARD_REQUEST_SUCCESS, if the target contract doesn't revert and all forwards are covered
     * @custom:member additionalStatusInfo:
     *   - If status is SUCCESS or FORWARD_REQUEST_SUCCESS, then this is empty.
     *   - If status is RECEIVER_FAILURE, this is `RETURNDATA_TRUNCATION_THRESHOLD` bytes of the
     *       return data (i.e. potentially truncated revert reason information).
     *   - If status is FORWARD_REQUEST_FAILURE, this is also the revert data - the reason the forward failed.
     *     This will be either an encoded Cancelled, DeliveryProviderReverted, or DeliveryProviderPaymentFailed error
     * @custom:member refundStatus - Result of the refund. REFUND_SUCCESS or REFUND_FAIL are for
     *     refunds where targetChain=refundChain; the others are for targetChain!=refundChain,
     *     where a cross chain refund is necessary
     * @custom:member overridesInfo:
     *   - If not an override: empty bytes array
     *   - Otherwise: An encoded `DeliveryOverride`
     */
    event Delivery(
        address indexed recipientContract,
        uint16 indexed sourceChain,
        uint64 indexed sequence,
        bytes32 deliveryVaaHash,
        DeliveryStatus status,
        uint256 gasUsed,
        RefundStatus refundStatus,
        bytes additionalStatusInfo,
        bytes overridesInfo
    );

    /**
     * @notice The delivery provider calls `deliver` to relay messages as described by one delivery instruction
     *
     * The delivery provider must pass in the specified (by VaaKeys[]) signed wormhole messages (VAAs) from the source chain
     * as well as the signed wormhole message with the delivery instructions (the delivery VAA)
     *
     * The messages will be relayed to the target address (with the specified gas limit and receiver value) iff the following checks are met:
     * - the delivery VAA has a valid signature
     * - the delivery VAA's emitter is one of these WormholeRelayer contracts
     * - the delivery provider passed in at least enough of this chain's currency as msg.value (enough meaning the maximum possible refund)
     * - the instruction's target chain is this chain
     * - the relayed signed VAAs match the descriptions in container.messages (the VAA hashes match, or the emitter address, sequence number pair matches, depending on the description given)
     *
     * @param encodedVMs - An array of signed wormhole messages (all from the same source chain
     *     transaction)
     * @param encodedDeliveryVAA - Signed wormhole message from the source chain's WormholeRelayer
     *     contract with payload being the encoded delivery instruction container
     * @param relayerRefundAddress - The address to which any refunds to the delivery provider
     *     should be sent
     * @param deliveryOverrides - Optional overrides field which must be either an empty bytes array or
     *     an encoded DeliveryOverride struct
     */
    function deliver(
        bytes[] memory encodedVMs,
        bytes memory encodedDeliveryVAA,
        address payable relayerRefundAddress,
        bytes memory deliveryOverrides
    ) external payable;
}

interface IWormholeRelayer is IWormholeRelayerDelivery, IWormholeRelayerSend {}

/*
 *  Errors thrown by IWormholeRelayer contract
 */

// Bound chosen by the following formula: `memoryWord * 4 + selectorSize`.
// This means that an error identifier plus four fixed size arguments should be available to developers.
// In the case of a `require` revert with error message, this should provide 2 memory word's worth of data.
uint256 constant RETURNDATA_TRUNCATION_THRESHOLD = 132;

//When msg.value was not equal to `delivery provider's quoted delivery price` + `paymentForExtraReceiverValue`
error InvalidMsgValue(uint256 msgValue, uint256 totalFee);

error RequestedGasLimitTooLow();

error DeliveryProviderDoesNotSupportTargetChain(
    address relayer,
    uint16 chainId
);
error DeliveryProviderCannotReceivePayment();

//When calling `forward()` on the WormholeRelayer if no delivery is in progress
error NoDeliveryInProgress();
//When calling `delivery()` a second time even though a delivery is already in progress
error ReentrantDelivery(address msgSender, address lockedBy);
//When any other contract but the delivery target calls `forward()` on the WormholeRelayer while a
//  delivery is in progress
error ForwardRequestFromWrongAddress(address msgSender, address deliveryTarget);

error InvalidPayloadId(uint8 parsed, uint8 expected);
error InvalidPayloadLength(uint256 received, uint256 expected);
error InvalidVaaKeyType(uint8 parsed);

error InvalidDeliveryVaa(string reason);
//When the delivery VAA (signed wormhole message with delivery instructions) was not emitted by the
//  registered WormholeRelayer contract
error InvalidEmitter(bytes32 emitter, bytes32 registered, uint16 chainId);
error VaaKeysLengthDoesNotMatchVaasLength(uint256 keys, uint256 vaas);
error VaaKeysDoNotMatchVaas(uint8 index);
//When someone tries to call an external function of the WormholeRelayer that is only intended to be
//  called by the WormholeRelayer itself (to allow retroactive reverts for atomicity)
error RequesterNotWormholeRelayer();

//When trying to relay a `DeliveryInstruction` to any other chain but the one it was specified for
error TargetChainIsNotThisChain(uint16 targetChain);
error ForwardNotSufficientlyFunded(
    uint256 amountOfFunds,
    uint256 amountOfFundsNeeded
);
//When a `DeliveryOverride` contains a gas limit that's less than the original
error InvalidOverrideGasLimit();
//When a `DeliveryOverride` contains a receiver value that's less than the original
error InvalidOverrideReceiverValue();
//When a `DeliveryOverride` contains a 'refund per unit of gas unused' that's less than the original
error InvalidOverrideRefundPerGasUnused();

//When the delivery provider doesn't pass in sufficient funds (i.e. msg.value does not cover the
// maximum possible refund to the user)
error InsufficientRelayerFunds(uint256 msgValue, uint256 minimum);

//When a bytes32 field can't be converted into a 20 byte EVM address, because the 12 padding bytes
//  are non-zero (duplicated from Utils.sol)
error NotAnEvmAddress(bytes32);

File 27 of 29 : Bytes.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 {
    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)
                // solhint-disable-next-line
                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) {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_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) {
        require(_bytes.length >= _start + 20, "toAddress_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) {
        require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(
        bytes memory _bytes,
        uint256 _start
    ) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(
        bytes memory _bytes,
        uint256 _start
    ) internal pure returns (uint32) {
        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(
        bytes memory _bytes,
        uint256 _start
    ) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(
        bytes memory _bytes,
        uint256 _start
    ) internal pure returns (uint96) {
        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(
        bytes memory _bytes,
        uint256 _start
    ) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(
        bytes memory _bytes,
        uint256 _start
    ) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(
        bytes memory _bytes,
        uint256 _start
    ) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, "toBytes32_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 28 of 29 : Formatters.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import { BytesLib } from "./Bytes.sol";
import {
    TradeAcceptMessage,
    TradeResultMessage,
    Assets,
    PayloadType,
    TradeOffer,
    AssetsSet,
    Result,
    AssetType,
    AssetData,
    TradeDetailed
} from "../utils/DataTypes.sol";

/// @title Encoder
/// @author NF3 Exchange
/// @dev This library defines helper functions used to encode structs to
///      array of bytes that can be used a payload in wormhole messages
library Encoder {
    /// -----------------------------------------------------------------------
    /// Library usage
    /// -----------------------------------------------------------------------

    using BytesLib for bytes;

    /// -----------------------------------------------------------------------
    /// Encoding functions
    /// -----------------------------------------------------------------------

    /// @dev Encode trade accept message to bytes array
    /// @param message Trade accept message that needs to be encoded
    function encodeTradeAcceptMessage(
        TradeAcceptMessage memory message
    ) internal pure returns (bytes memory) {
        return
            abi.encodePacked(
                message.payloadType,
                message.tradeOfferHash,
                _encodeAssets(message.offeringAssets),
                _encodeAssets(message.considerationAssets),
                message.seller,
                message.buyer,
                message.primaryChainId,
                message.secondaryChainId
            );
    }

    /// @dev Encode trade result message to bytes array
    /// @param message Trade result message that needs to be encoded
    function encodeTradeResultMessage(
        TradeResultMessage memory message
    ) internal pure returns (bytes memory) {
        return
            abi.encodePacked(
                message.payloadType,
                message.tradeOfferHash,
                message.result,
                message.primaryChainId,
                message.secondaryChainId
            );
    }

    /// @dev Convert a trade offer struct to bytes32 hash
    /// @param _offer Trade offer struct that needs to be hashed
    function hashTradeOffer(
        TradeOffer memory _offer
    ) internal pure returns (bytes32) {
        bytes32 _hash;

        _hash = _hashAssetsSet(_offer.offeringAssets, _hash);
        _hash = _hashAssetsSet(_offer.considerationAssets, _hash);
        _hash = keccak256(
            abi.encodePacked(
                _offer.owner,
                _offer.timePeriod,
                _offer.nonce,
                _offer.tradeIntendedFor,
                _offer.primaryChainId,
                _offer.secondaryChainId,
                _hash
            )
        );

        return _hash;
    }

    /// @dev Convert a trade contants data to bytes32 hash
    /// @param offeringAssets Assets that are being offered by the seller
    /// @param considerationAssets Assets that are begin asked by the buyer
    /// @param buyer Address of the buyer
    /// @param seller Address of the seller
    function hashTradeConstants(
        Assets memory offeringAssets,
        Assets memory considerationAssets,
        address buyer,
        address seller
    ) internal pure returns (bytes32) {
        bytes32 _hash;

        _hash = keccak256(
            abi.encodePacked(_encodeAssets(offeringAssets), _hash)
        );
        _hash = keccak256(
            abi.encodePacked(_encodeAssets(considerationAssets), _hash)
        );

        _hash = keccak256(abi.encodePacked(buyer, seller, _hash));

        return _hash;
    }

    /// -----------------------------------------------------------------------
    /// Helper methods
    /// -----------------------------------------------------------------------

    /// @dev Encode Assets data type to bytes array
    /// @param _assets Assets that need to be encoded
    function _encodeAssets(
        Assets memory _assets
    ) private pure returns (bytes memory data) {
        /*
         * Storage Slots Used
         * 1 slot => 32 bytes for lenght of assets
         * 3 slots => 96 bytes for each of AssetData struct
         * total = 32 bytes + assetCount * 96 bytes
         */

        bytes memory packedAssets;

        uint len = _assets.assets.length;
        for (uint i; i < len; ) {
            packedAssets = abi.encodePacked(
                packedAssets,
                _assets.assets[i].token,
                _assets.assets[i].assetType,
                _assets.assets[i].tokenId,
                _assets.assets[i].amount
            );
            unchecked {
                ++i;
            }
        }

        return abi.encodePacked(_assets.assets.length, packedAssets);
    }

    /// @dev convert AssetsSet struct to bytes32 hash
    /// @param assetsSet AssetsSet struct that needs to be hashed
    /// @param _hash previous hash value to be encoded with new hash
    function _hashAssetsSet(
        AssetsSet memory assetsSet,
        bytes32 _hash
    ) private pure returns (bytes32) {
        _hash = keccak256(
            abi.encodePacked(_encodeAssets(assetsSet.primaryChainAssets), _hash)
        );
        _hash = keccak256(
            abi.encodePacked(
                _encodeAssets(assetsSet.secondaryChainAssets),
                _hash
            )
        );

        return _hash;
    }
}

/// @title Decoder
/// @author NF3 Exchange
/// @dev This library defines helper functions used to decode bytes
///      arrays encoded by Encoder library defined above
library Decoder {
    /// -----------------------------------------------------------------------
    /// Library usage
    /// -----------------------------------------------------------------------

    using BytesLib for bytes;

    /// -----------------------------------------------------------------------
    /// Decoding functions
    /// -----------------------------------------------------------------------

    /// @dev Decode trade accept message from bytes array to TradeAcceptMessage struct
    /// @param encoded Recieved bytes array to be decoded
    function decodeTradeAcceptMessage(
        bytes memory encoded
    ) internal pure returns (TradeAcceptMessage memory) {
        uint256 offset = 0;

        uint8 _paylodType = encoded.toUint8(offset);
        PayloadType payloadType = PayloadType(_paylodType);
        offset += 1;

        bytes32 tradeOfferHash = encoded.toBytes32(offset);

        offset += 32;

        (Assets memory offeringAssets, uint256 _offset) = _decodeAssets(
            encoded,
            offset
        );
        offset = _offset;

        (Assets memory considerationAssets, uint256 __offset) = _decodeAssets(
            encoded,
            offset
        );

        offset = __offset;
        address seller = encoded.toAddress(offset);
        offset += 20;
        address buyer = encoded.toAddress(offset);
        offset += 20;

        uint16 primaryChainId = encoded.toUint16(offset);
        offset += 2;
        uint16 secondaryChainId = encoded.toUint16(offset);
        offset += 2;

        return
            TradeAcceptMessage(
                payloadType,
                tradeOfferHash,
                offeringAssets,
                considerationAssets,
                seller,
                buyer,
                primaryChainId,
                secondaryChainId
            );
    }

    /// @dev Decode trade result message from bytes array to TradeResultMessage struct
    /// @param encoded Recieved bytes array to be decoded
    function decodeTradeResultMessage(
        bytes memory encoded
    ) internal pure returns (TradeResultMessage memory) {
        uint256 offset = 0;
        uint8 _paylodType = encoded.toUint8(offset);
        PayloadType payloadType = PayloadType(_paylodType);
        offset += 1;
        bytes32 tradeOfferHash = encoded.toBytes32(offset);
        offset += 32;
        uint8 _type = encoded.toUint8(offset);
        Result result = Result(_type);
        offset += 1;
        uint16 primaryChainId = encoded.toUint16(offset);
        offset += 2;
        uint16 secondaryChainId = encoded.toUint16(offset);

        return
            TradeResultMessage(
                payloadType,
                tradeOfferHash,
                result,
                primaryChainId,
                secondaryChainId
            );
    }

    /// @dev Decode payload type from received encoded bytes array
    /// @param encoded Bytes string of the entire message
    function getPayloadType(
        bytes memory encoded
    ) internal pure returns (PayloadType) {
        uint256 offset = 0;
        uint8 _payloadType = encoded.toUint8(offset);

        return PayloadType(_payloadType);
    }

    /// -----------------------------------------------------------------------
    /// Helper methods
    /// -----------------------------------------------------------------------

    /// @dev Decode Assets data type from bytes array
    /// @param encoded Bytes string of the entire message
    /// @param offset Position of assets struct in the message string
    function _decodeAssets(
        bytes memory encoded,
        uint256 offset
    ) private pure returns (Assets memory, uint256 _offset) {
        // first 32 bytes for assetData array length
        uint256 assetsCount = encoded.toUint256(offset);
        offset += 32;

        // remaining bytes are divided as
        // 1. fist 20 bytes for token address
        // 2. next 1 bytes for assetType enum
        // 3. next 32 bytes for tokenId of the asset
        // 4. next 32 bytes for amount of asset
        AssetData[] memory assets = new AssetData[](assetsCount);
        for (uint i; i < assetsCount; ) {
            address _token = encoded.toAddress(offset);
            offset += 20;
            uint8 _type = encoded.toUint8(offset);
            offset += 1;
            uint256 _tokenId = encoded.toUint256(offset);
            offset += 32;
            uint256 _amount = encoded.toUint256(offset);
            offset += 32;

            assets[i] = AssetData({
                token: _token,
                assetType: AssetType(_type),
                tokenId: _tokenId,
                amount: _amount
            });

            unchecked {
                ++i;
            }
        }

        return (Assets({ assets: assets }), offset);
    }
}

File 29 of 29 : DataTypes.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol";

// -----------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------

uint8 constant SELLER_BITMASK = 0x1; // 00000001
uint8 constant BUYER_BITMASK = 0x2; // 00000010
uint256 constant GAS_REQUIRED_FOR_ERC20 = 60_000;
uint256 constant GAS_REQUIRED_FOR_ERC721 = 80_000;
uint256 constant GAS_REQUIRED_FOR_ERC1155 = 60_000;
uint256 constant BASE_GAS_REQUIRED = 250_000;
uint256 constant BASE_GAS_FOR_FORWARDING = 150_000;

// -----------------------------------------------------------------------
// Typed structs
// -----------------------------------------------------------------------

/// @dev Common Assets type, packing bundle of NFTs and FTs.
/// @param assets Array of individual tokens along with their details
struct Assets {
    AssetData[] assets;
}

/// @dev Type for individual asset along with it's details
/// @param token token address
/// @param assetType type of asset represented by the address
/// @param tokenId tokenId in case of ERC721 and ERC1155 tokens. Set to 0 in case of ERC20 token
/// @param amount amount of token for ERC20 and ERC1155 tokens. Set to 1 in case of ERC721 token
struct AssetData {
    address token;
    AssetType assetType;
    uint256 tokenId;
    uint256 amount;
}

/// @dev Set of assets on both the chains, representing one side of offering by the user
/// @param primaryChainAssets Assets existing on primary chain of the trade
/// @param secondaryChainAssets Assets exisitng on secondary chain of
struct AssetsSet {
    Assets primaryChainAssets;
    Assets secondaryChainAssets;
}

/// @dev Fees to be sent to admin
/// @param to Address which will receive the fees
/// @param token ERC20 token address used for fees
/// @param amount Amount of token to be transferred
struct Fees {
    address to;
    address token;
    uint256 amount;
}

/// @dev Trade offer proposed by one of the user
/// @param offeringAssets AssetsSet offered by the seller
/// @param considerationAssets AssetsSet requested from the buyer
/// @param buyerFees Fee to be paid by the buyer
/// @param sellerFees Fee to be paid by the seller
/// @param owner Owner of the trade offer. aka seller
/// @param timePeriod Expiration timestamp of the offer
/// @param nonce Nonce of the trade
/// @param tradeIntendedFor Address to which the trade is targetted to. To be used in case of
///        private P2P trades. Can be set to address(0) to allow anyone to accept the trade
/// @param primaryChainId Wormhole chain Id of the primary chain
/// @param secondaryChainId Wormhole chain Id of the secondary chain
struct TradeOffer {
    AssetsSet offeringAssets;
    AssetsSet considerationAssets;
    Fees buyerFees;
    Fees sellerFees;
    address owner;
    uint256 timePeriod;
    uint256 nonce;
    address tradeIntendedFor;
    uint256 primaryChainId; // wormhole chain Id
    uint256 secondaryChainId; // wormhole chain Id
}

/// @dev Compressed trade data that needs to be stored on chain for each trade
///      consumes 2 slots in the storage
/// @param tradeConstantsHash Hash of the constants involved in the trade
/// @param status Current status of the trade
/// @param withdrawBitmap bit 0 represents seller's withdraw status. but 1 represnts
///        buyer's withdraw status. Used to ensure seller and buyer each withdraw only once
/// @param recoveryRequested flag used to mark if recovery of the trade is requested
struct Trade {
    bytes32 tradeContantsHash; // size 32 bytes => slot 1
    Status status; // size 1 byte => slot 2
    uint8 withdrawBitmap; // size 1 byte => slot 2
    bool recoveryRequested; // size 1 byte => slot 2
}

/// @dev Helper struct packing all the constatns involved in a trade
/// @param offeringAssets Assets offered on the current chain by the seller
/// @param considerationAssets Assets requested by the seller on the current chain
/// @param seller Address of the seller
/// @param buyer Address of the buyer
struct TradeConstants {
    Assets offeringAssets;
    Assets considerationAssets;
    address seller;
    address buyer;
}

/// @dev Actual trade data with all the details of the trade
/// @param tradeOfferHash Hash of the acutal trade offer used as uinque id.
///        Using hash because impossible to keep id's on multiple chains in sync
/// @param offeringAssets Assets offered on the current chain by the seller
/// @param considerationAssets Assets requested by the seller on the current chain
/// @param seller Address of the seller
/// @param status Current status of the trade
/// @param buyer Address of the buyer
/// @param withdrawBitmap bit 0 represents seller's withdraw status. but 1 represnts
///        buyer's withdraw status. Used to ensure seller and buyer each withdraw only once
/// @param recoveryRequested flag used to mark if recovery of the trade is requested
struct TradeDetailed {
    bytes32 tradeOfferHash;
    Assets offeringAssets;
    Assets considerationAssets;
    address seller;
    Status status;
    address buyer;
    uint8 withdrawBitmap;
    bool recoveryRequested;
}

/// @dev wormhole to be emmited when a trade has been accepted on one chain and needs
///      to be processed on other chain
/// @param payloadType Type of payload sent in the message. Used for selecting appropriate
///        function while receiving wormhole messages
/// @param tradeOfferHash Hash of the trade offer. Used as id
/// @param offeringAssets Assets offered on current chain
/// @param considerationAssets Assets requested on current chain
/// @param seller Address of the seller
/// @param buyer Address of the buyer
/// @param primaryChainId wormhole chain id of the primary chain
/// @param secondaryChainId wormhole chain id of the secondary chain
struct TradeAcceptMessage {
    PayloadType payloadType;
    bytes32 tradeOfferHash;
    Assets offeringAssets;
    Assets considerationAssets;
    address seller;
    address buyer;
    uint16 primaryChainId; // wormhole chain Id
    uint16 secondaryChainId; // wormhole chain Id
}

/// @dev wormhole message to be emmited when a trade has completed processing on
///      the secondary chain. Used to mark result of the trade
/// @param payloadType Type of payload sent in the message. Used for selecting appropriate
///        function while receiving wormhole messages
/// @param tradeOfferHash Hash of the trade offer. Used as id
/// @param result Final result of the trade
/// @param primaryChainId wormhole chain id of the primary chain
/// @param secondaryChainId wormhole chain id of the secondary chain
struct TradeResultMessage {
    PayloadType payloadType;
    bytes32 tradeOfferHash;
    Result result;
    uint16 primaryChainId; // wormhole chain Id
    uint16 secondaryChainId; // wormhole chain Id
}

/// @dev Struct to pack data regarding the gas forwarding during wormhole
///      message passing
/// @param forwardingValue Value to be passed in the message
/// @param nonce Nonce of the message
/// @param owner Owner of the message
struct ForwardingGas {
    uint256 forwardingValue;
    uint256 nonce;
    address owner;
}

/// @dev Struct to pack storage state for the protcol
/// @param wormholeRelayer wormhole core contract address
/// @param consistencyLevel number of confirmations for wormhole messages
/// @param gasSignatureAdmin admin's address who will sign the gas signatures
/// @param wormholeChainId wormhole chain id of current network
/// @param vaultAddress assets vault for the system
/// @param targetContractToChainId mapping from target contract on other chain to
///        their wormhole chain id
/// @param targetChainIdToContract mapping from wormhole chain id to target contract
///        on other chain
/// @param nonce Mapping of users and their nonce in form of bitmap
/// @param gasForwardingNonce Bitmap for gas forwarding struct of admin signature
/// @param trades trade data mapped to it's hash
/// @param consumedMessages consumed messages, used to prevent reentrancy and
///        replay
/// @param types mapping of token addresses and their Types
struct State {
    address wormholeRelayer;
    uint8 consistencyLevel;
    address gasSignatureAdmin;
    uint16 wormholeChainId;
    address vaultAddress;
    mapping(address => uint16) targetContractToChainId;
    mapping(uint16 => address) targetChainIdToContract;
    mapping(address => BitMaps.BitMap) nonce;
    BitMaps.BitMap gasForwardingNonce;
    mapping(bytes32 => Trade) trades;
    mapping(bytes32 => bool) consumedMessages;
    mapping(address => AssetType) types;
}

// -----------------------------------------------------------------------
// Enums
// -----------------------------------------------------------------------

enum Status {
    IDLE, // default status
    PENDING, // pulled funds on primary chain and sent message to secondary chain
    FAILED, // Could not pull funds on other chain so will revert on primary chain
    SUCCESS // succesfully pulled funds on both the chains and swapped assets can now be withdrawn
}

enum Result {
    SUCCESS, // trade was succesfull on secondary chain
    FAILED // trade failed on secondary chain
}

enum PayloadType {
    PROCESS_TRADE, // function selector for processing trade on secondary chain
    COMPLETE_TRADE // function selector for completing trade on primary chain
}

enum AssetType {
    INVALID,
    ERC_20,
    ERC_721,
    ERC_1155
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"enum CrossChainSwapsSignatureUtil.SigningUtilsErrorCodes","name":"code","type":"uint8"}],"name":"SigningUtilsError","type":"error"},{"inputs":[{"internalType":"enum ICrossChainSwaps.SwapErrorCode","name":"code","type":"uint8"}],"name":"SwapError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"AssetsWithdrew","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"}],"name":"RecoveryRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"targetContract","type":"address"},{"indexed":false,"internalType":"uint16","name":"targetChainId","type":"uint16"}],"name":"TargetContractAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"enum AssetType[]","name":"types","type":"uint8[]"}],"name":"TokensTypeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"indexed":false,"internalType":"enum Status","name":"status","type":"uint8"}],"name":"TradeCompleted","type":"event"},{"anonymous":false,"inputs":[{"components":[{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"primaryChainAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"secondaryChainAssets","type":"tuple"}],"internalType":"struct AssetsSet","name":"offeringAssets","type":"tuple"},{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"primaryChainAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"secondaryChainAssets","type":"tuple"}],"internalType":"struct AssetsSet","name":"considerationAssets","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Fees","name":"buyerFees","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Fees","name":"sellerFees","type":"tuple"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"timePeriod","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"tradeIntendedFor","type":"address"},{"internalType":"uint256","name":"primaryChainId","type":"uint256"},{"internalType":"uint256","name":"secondaryChainId","type":"uint256"}],"indexed":false,"internalType":"struct TradeOffer","name":"offer","type":"tuple"},{"indexed":true,"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"offeringAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"considerationAssets","type":"tuple"},{"internalType":"address","name":"seller","type":"address"},{"internalType":"enum Status","name":"status","type":"uint8"},{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint8","name":"withdrawBitmap","type":"uint8"},{"internalType":"bool","name":"recoveryRequested","type":"bool"}],"indexed":false,"internalType":"struct TradeDetailed","name":"trade","type":"tuple"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"wormholeSequence","type":"uint256"}],"name":"TradeInitialized","type":"event"},{"anonymous":false,"inputs":[{"components":[{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"primaryChainAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"secondaryChainAssets","type":"tuple"}],"internalType":"struct AssetsSet","name":"offeringAssets","type":"tuple"},{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"primaryChainAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"secondaryChainAssets","type":"tuple"}],"internalType":"struct AssetsSet","name":"considerationAssets","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Fees","name":"buyerFees","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Fees","name":"sellerFees","type":"tuple"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"timePeriod","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"tradeIntendedFor","type":"address"},{"internalType":"uint256","name":"primaryChainId","type":"uint256"},{"internalType":"uint256","name":"secondaryChainId","type":"uint256"}],"indexed":false,"internalType":"struct TradeOffer","name":"offer","type":"tuple"}],"name":"TradeOfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"offeringAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"considerationAssets","type":"tuple"},{"internalType":"address","name":"seller","type":"address"},{"internalType":"enum Status","name":"status","type":"uint8"},{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint8","name":"withdrawBitmap","type":"uint8"},{"internalType":"bool","name":"recoveryRequested","type":"bool"}],"indexed":false,"internalType":"struct TradeDetailed","name":"trade","type":"tuple"},{"indexed":false,"internalType":"enum Status","name":"status","type":"uint8"},{"indexed":false,"internalType":"uint64","name":"wormholeSequence","type":"uint64"}],"name":"TradeProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"indexed":false,"internalType":"enum Status","name":"status","type":"uint8"}],"name":"TradeRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"address","name":"targetContractAddress_","type":"address"},{"internalType":"uint16","name":"targetChainId_","type":"uint16"}],"name":"addTargetContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"offeringAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"considerationAssets","type":"tuple"}],"name":"calculateGasForRelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"primaryChainAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"secondaryChainAssets","type":"tuple"}],"internalType":"struct AssetsSet","name":"offeringAssets","type":"tuple"},{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"primaryChainAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"secondaryChainAssets","type":"tuple"}],"internalType":"struct AssetsSet","name":"considerationAssets","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Fees","name":"buyerFees","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Fees","name":"sellerFees","type":"tuple"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"timePeriod","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"tradeIntendedFor","type":"address"},{"internalType":"uint256","name":"primaryChainId","type":"uint256"},{"internalType":"uint256","name":"secondaryChainId","type":"uint256"}],"internalType":"struct TradeOffer","name":"_offer","type":"tuple"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"cancelTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"consistencyLevel","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasSignatureAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"getGasNonce","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"getNonce","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wormholeRelayer_","type":"address"},{"internalType":"uint16","name":"wormholeChainId_","type":"uint16"},{"internalType":"uint8","name":"consistencyLevel_","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"primaryChainAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"secondaryChainAssets","type":"tuple"}],"internalType":"struct AssetsSet","name":"offeringAssets","type":"tuple"},{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"primaryChainAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"secondaryChainAssets","type":"tuple"}],"internalType":"struct AssetsSet","name":"considerationAssets","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Fees","name":"buyerFees","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Fees","name":"sellerFees","type":"tuple"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"timePeriod","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"tradeIntendedFor","type":"address"},{"internalType":"uint256","name":"primaryChainId","type":"uint256"},{"internalType":"uint256","name":"secondaryChainId","type":"uint256"}],"internalType":"struct TradeOffer","name":"_offer","type":"tuple"},{"internalType":"bytes","name":"offerSignature","type":"bytes"},{"components":[{"internalType":"uint256","name":"forwardingValue","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"internalType":"struct ForwardingGas","name":"_forwardingGas","type":"tuple"},{"internalType":"bytes","name":"gasSignature","type":"bytes"}],"name":"initializeTrade","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"messageHashConsumed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"bytes[]","name":"","type":"bytes[]"},{"internalType":"bytes32","name":"sourceAddress","type":"bytes32"},{"internalType":"uint16","name":"sourceChain","type":"uint16"},{"internalType":"bytes32","name":"deliveryHash","type":"bytes32"}],"name":"receiveWormholeMessages","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"internalType":"enum Result","name":"result","type":"uint8"}],"name":"recoverTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"offeringAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"considerationAssets","type":"tuple"},{"internalType":"address","name":"seller","type":"address"},{"internalType":"address","name":"buyer","type":"address"}],"internalType":"struct TradeConstants","name":"tradeConstants","type":"tuple"}],"name":"requestRecovery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"consistencyLevel_","type":"uint8"}],"name":"setConsistencyLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gasSignatureAdmin_","type":"address"}],"name":"setGasSignatureAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"enum AssetType[]","name":"_types","type":"uint8[]"}],"name":"setTokenTypes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vaultAddress_","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"wormholeChainId_","type":"uint16"}],"name":"setWormholeChainId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wormholeRelayer_","type":"address"}],"name":"setWormholeRelayer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_chainId","type":"uint16"}],"name":"targetChainIdToContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"targetContractToChainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_hash","type":"bytes32"}],"name":"trades","outputs":[{"components":[{"internalType":"bytes32","name":"tradeContantsHash","type":"bytes32"},{"internalType":"enum Status","name":"status","type":"uint8"},{"internalType":"uint8","name":"withdrawBitmap","type":"uint8"},{"internalType":"bool","name":"recoveryRequested","type":"bool"}],"internalType":"struct Trade","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vaultAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"_assets","type":"tuple"}],"name":"verifyAssetWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tradeOfferHash","type":"bytes32"},{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"offeringAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"considerationAssets","type":"tuple"},{"internalType":"address","name":"seller","type":"address"},{"internalType":"address","name":"buyer","type":"address"}],"internalType":"struct TradeConstants","name":"tradeConstants","type":"tuple"}],"name":"withdrawTradeAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wormholeChainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wormholeRelayer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

6101406040523480156200001257600080fd5b506040805169082e6e6cae888c2e8c2560b31b60208201526d1859191c995cdcc81d1bdad95b8b60921b602a82018190526f1d5a5b9d0e08185cdcd95d151e5c194b60821b60388301526f1d5a5b9d0c8d4d881d1bdad95b92590b60821b60488301526d1d5a5b9d0c8d4d88185b5bdd5b9d60921b60588301819052602960f81b606684018190528451604781860301815260678501865266082e6e6cae8e6560cb1b60878601527f4173736574446174615b5d206173736574732900000000000000000000000000608e8601528551608181870301815260a18601875269082e6e6cae8e6a6cae8560b31b60c18701527f417373657473207072696d617279436861696e4173736574732c00000000000060cb8701527f417373657473207365636f6e64617279436861696e417373657473000000000060e58701526101008601839052865160e1818803018152610101870188526408ccacae6560db1b6101218801526a1859191c995cdcc81d1bcb60aa1b61012688015261013187019590955261013f86019390935261014d8501829052855161012e81870301815261014e860187526a0a8e4c2c8ca9ecccccae4560ab1b61016e8701527f417373657473536574206f66666572696e674173736574732c000000000000006101798701527f41737365747353657420636f6e73696465726174696f6e4173736574732c00006101928701526e1199595cc8189d5e595c9199595ccb608a1b6101b08701526f1199595cc81cd95b1b195c9199595ccb60821b6101bf8701526d1859191c995cdcc81bdddb995c8b60921b6101cf8701527f75696e743235362074696d65506572696f642c000000000000000000000000006101dd8701526d1d5a5b9d0c8d4d881b9bdb98d94b60921b6101f087018190527f61646472657373207472616465496e74656e646564466f722c000000000000006101fe8801527f75696e74323536207072696d617279436861696e49642c0000000000000000006102178801527f75696e74323536207365636f6e64617279436861696e4964290000000000000061022e8801528751610227818903018152610247880189526d08cdee4eec2e4c8d2dcce8ec2e6560931b6102678901527f75696e7432353620666f7277617264696e6756616c75652c000000000000000061027589015261028d8801919091526c30b2323932b9b99037bbb732b960991b61029b8801526102a887019390935286516102898188030181526102a9870190975290959294909290620003c990839088908890889088906102c901620004a7565b60408051601f1981840301815290829052805160209182012060805282518382012060a05284518582012060c052620004099186918991899101620004ec565b60408051601f1981840301815290829052805160209182012060e052620004359187918991016200050b565b60408051601f1981840301815291905280516020918201206101005286519601959095206101205250620005259350505050565b6000815160005b818110156200048c576020818501810151868301520162000470565b818111156200049c576000828601525b509290920192915050565b6000620004e1620004da620004d3620004cc620004c5868c62000469565b8a62000469565b8862000469565b8662000469565b8462000469565b979650505050505050565b600062000502620004da620004d3848862000469565b95945050505050565b60006200051d620004da838662000469565b949350505050565b60805160a05160c05160e0516101005161012051615929620005756000396000613f8c01526000613d5001526000613ad001526000613b350152600061389701526000612f4201526159296000f3fe6080604052600436106101e25760003560e01c8063751a0c6411610102578063cf40827d11610095578063e8dfd50811610064578063e8dfd508146105fe578063f2fde38b1461063b578063f7503fc41461065b578063f763c1fc1461067b57600080fd5b8063cf40827d14610580578063d61bd89a146105a0578063da25b725146105c0578063e159378d146105de57600080fd5b80638da5cb5b116100d15780638da5cb5b146104f157806392e08a6b1461050f5780639c2aa8691461052f578063a87f32791461054f57600080fd5b8063751a0c6414610458578063793e64e31461047857806384b0196e146104a957806389535803146104d157600080fd5b806351ab15751161017a5780635e8345b3116101495780635e8345b3146103d95780635ffaf562146104055780636817031b14610423578063715018a61461044357600080fd5b806351ab15751461036e578063529dca321461038e578063538ee295146103a15780635c975abb146103c157600080fd5b80633990264e116101b65780633990264e146102ee578063430bf08a146103105780634a7d85171461032e5780634f5b96c71461034e57600080fd5b8062162420146101e757806309dfa5721461021d57806323602e5a146102705780632fbb792b146102be575b600080fd5b3480156101f357600080fd5b50610207610202366004614308565b6106a9565b6040516102149190614360565b60405180910390f35b34801561022957600080fd5b506102586102383660046143b4565b61ffff16600090815261010160205260409020546001600160a01b031690565b6040516001600160a01b039091168152602001610214565b34801561027c57600080fd5b506102ab61028b3660046143e6565b6001600160a01b03166000908152610100602052604090205461ffff1690565b60405161ffff9091168152602001610214565b3480156102ca57600080fd5b506102de6102d93660046145ed565b610749565b6040519015158152602001610214565b3480156102fa57600080fd5b5061030e610309366004614633565b610803565b005b34801561031c57600080fd5b5060ff546001600160a01b0316610258565b34801561033a57600080fd5b5061030e610349366004614676565b610a9a565b34801561035a57600080fd5b506102de610369366004614308565b610e6a565b34801561037a57600080fd5b5061030e6103893660046143e6565b610e90565b61030e61039c366004614734565b610ea4565b3480156103ad57600080fd5b5061030e6103bc36600461482a565b610f86565b3480156103cd57600080fd5b5060975460ff166102de565b6103ec6103e736600461485e565b610fd5565b60405167ffffffffffffffff9091168152602001610214565b34801561041157600080fd5b5060fe546001600160a01b0316610258565b34801561042f57600080fd5b5061030e61043e3660046143e6565b61136d565b34801561044f57600080fd5b5061030e61137e565b34801561046457600080fd5b5061030e6104733660046143b4565b611392565b34801561048457600080fd5b5060fe5474010000000000000000000000000000000000000000900461ffff166102ab565b3480156104b557600080fd5b506104be6113e2565b6040516102149796959493929190614979565b3480156104dd57600080fd5b506102de6104ec366004614a2b565b6114a4565b3480156104fd57600080fd5b506033546001600160a01b0316610258565b34801561051b57600080fd5b5061030e61052a366004614a55565b6114df565b34801561053b57600080fd5b5061030e61054a366004614676565b61160d565b34801561055b57600080fd5b506102de61056a366004614308565b6000908152610105602052604090205460ff1690565b34801561058c57600080fd5b5061030e61059b366004614a7e565b6117ae565b3480156105ac57600080fd5b5061030e6105bb366004614af6565b61180a565b3480156105cc57600080fd5b5060fd546001600160a01b0316610258565b3480156105ea57600080fd5b5061030e6105f93660046143e6565b611853565b34801561060a57600080fd5b5060fd5474010000000000000000000000000000000000000000900460ff1660405160ff9091168152602001610214565b34801561064757600080fd5b5061030e6106563660046143e6565b611864565b34801561066757600080fd5b5061030e610676366004614b62565b6118f1565b34801561068757600080fd5b5061069b610696366004614bd8565b611a03565b604051908152602001610214565b60408051608081018252600080825260208201819052918101829052606081019190915260008281526101046020908152604091829020825160808101909352805483526001810154909183019060ff16600381111561070b5761070b614321565b600381111561071c5761071c614321565b81526001919091015460ff6101008204811660208401526201000090910416151560409091015292915050565b80515160009081905b808210156107f957835180518390811061076e5761076e614c32565b602002602001015160200151600381111561078b5761078b614321565b8451805161010691600091869081106107a6576107a6614c32565b602090810291909101810151516001600160a01b031682528101919091526040016000205460ff1660038111156107df576107df614321565b146107ee575060009392505050565b816001019150610752565b5060019392505050565b600054610100900460ff16158080156108235750600054600160ff909116105b8061083d5750303b15801561083d575060005460ff166001145b6108b45760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561091257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b61091b84611a3e565b60fd80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff85160217905560fe80547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000061ffff8616021790556109ac611aa2565b6109b4611b27565b6109bc611bac565b610a306040518060400160405280601481526020017f4e46332043726f7373636861696e2053776170730000000000000000000000008152506040518060400160405280600581526020017f302e322e30000000000000000000000000000000000000000000000000000000815250611c31565b8015610a9457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15b50505050565b610aa2611cb8565b610aaa611d0b565b600082815261010460209081526040808320815160808101909252805482526001810154919290919083019060ff166003811115610aea57610aea614321565b6003811115610afb57610afb614321565b81526001919091015460ff610100820481166020840152620100009091041615156040909101529050600381602001516003811115610b3c57610b3c614321565b14158015610b605750600281602001516003811115610b5d57610b5d614321565b14155b15610b8157600960405163b752672760e01b81526004016108ab9190614c61565b610b8b8183611d64565b610b9b60808301606084016143e6565b6001600160a01b03163303610cdd57604081015160021615610bd357600a60405163b752672760e01b81526004016108ab9190614c61565b60ff546001600160a01b031663d7f5dcf2600383602001516003811115610bfc57610bfc614321565b14610c1357610c0e6020850185614c7b565b610c1d565b610c1d8480614c7b565b610c2d60808601606087016143e6565b6040518363ffffffff1660e01b8152600401610c4a929190614d95565b6020604051808303816000875af1158015610c69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c8d9190614dc0565b5060008381526101046020526040902060010180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff811660026101009283900460ff1617909102179055610e2e565b610ced60608301604084016143e6565b6001600160a01b03163303610e2e57604081015160011615610d2557600b60405163b752672760e01b81526004016108ab9190614c61565b60ff546001600160a01b031663d7f5dcf2600383602001516003811115610d4e57610d4e614321565b14610d6257610d5d8480614c7b565b610d6f565b610d6f6020850185614c7b565b610d7f60608601604087016143e6565b6040518363ffffffff1660e01b8152600401610d9c929190614d95565b6020604051808303816000875af1158015610dbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ddf9190614dc0565b50600083815261010460205260409020600190810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff81166101009182900460ff16909317029190911790555b604051339084907f277b97242b69f3f0ed095a3b632f6929f0d4edf01019ccd1ab48db0b4f5ef0b290600090a350610e666001606555565b5050565b600881901c60009081526101036020526040812054600160ff84161b1615155b92915050565b610e98611ddf565b610ea181611e39565b50565b610eac611e9d565b610eb7838383611ed4565b6000610ec286611fd2565b90506000816001811115610ed857610ed8614321565b03610f66576000610ee887611ff7565b9050600080600080610ef98561215b565b93509350935093506000610f12858a8860a001516123ba565b90508115610f5b5785602001517f133006c2d7c0bc372186e62dd70325ca161419a7c99165a403d04f29eace5c17858584604051610f5293929190614ef8565b60405180910390a25b505050505050610f7e565b6000610f7187612479565b9050610f7c816125ac565b505b505050505050565b610f8e611ddf565b60fd80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff84160217905550565b6000610fdf611cb8565b610fe7611d0b565b610ff3858585856126db565b600061100661100187615026565b6128f3565b60408051610100810190915281815290915060009060208101611029898061510d565b6110339080614c7b565b61103c90615141565b8152602001888060200190611051919061510d565b61105b9080614c7b565b61106490615141565b815260200161107b6101208a016101008b016143e6565b6001600160a01b0316815260200160018152602001336001600160a01b03908116825260006020830181905260409283015260ff5482517f6195687300000000000000000000000000000000000000000000000000000000815293945016916361956873916110f69185918c019060a08d0190600401615181565b6020604051808303816000875af1158015611115573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111399190614dc0565b5061118d61114f61012089016101008a016143e6565b8861014001356001600160a01b038216600090815261010260209081526040808320600885901c845290915290208054600160ff84161b1790555050565b6111b88560200135600881901c6000908152610103602052604090208054600160ff84161b17905550565b6111c1816129ba565b600061128a604051806101000160405280600060018111156111e5576111e5614321565b8152602081018690526040016111fb8b8061510d565b611209906020810190614c7b565b61121290615141565b81526020018a8060200190611227919061510d565b611235906020810190614c7b565b61123e90615141565b815260200184606001516001600160a01b031681526020018460a001516001600160a01b031681526020018a610180013561ffff1681526020018a6101a0013561ffff16815250612a82565b90506112f58161129a8a8061510d565b6112a8906020810190614c7b565b6112b560208c018c61510d565b6112c3906020810190614c7b565b6101a08c013561ffff8116600090815261010160205260409020546001600160a01b0316906101808e01358c35612ae9565b935061130961012089016101008a016143e6565b6001600160a01b0316336001600160a01b0316847fb31aaaa4ab267f40fb7e7a643dc5aabab8ac321fc7010469bf681f2970a9a0638b86896040516113509392919061532c565b60405180910390a45050506113656001606555565b949350505050565b611375611ddf565b610ea181612c5b565b611386611ddf565b6113906000612cbf565b565b61139a611ddf565b60fe80547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000061ffff84160217905550565b60006060806000806000606060c9546000801b148015611402575060ca54155b61144e5760405162461bcd60e51b815260206004820152601560248201527f4549503731323a20556e696e697469616c697a6564000000000000000000000060448201526064016108ab565b611456612d29565b61145e612dbb565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b6001600160a01b038216600090815261010260209081526040808320600885901c8452909152812054600160ff84161b1615155b9392505050565b6114e7611ddf565b60008281526101046020526040902060018082015460ff16600381111561151057611510614321565b1461153157600960405163b752672760e01b81526004016108ab9190614c61565b600181015462010000900460ff1661155f57600e60405163b752672760e01b81526004016108ab9190614c61565b600082600181111561157357611573614321565b1461157f576002611582565b60035b6001808301805490917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116908360038111156115c2576115c2614321565b0217905550600181015460405184917f08a6782de64ec46be9fdf6cb9403f0c945f92ca67c1e426388abe40d25dc1000916116009160ff169061536c565b60405180910390a2505050565b60008281526101046020526040902060018082015460ff16600381111561163657611636614321565b1461165757600960405163b752672760e01b81526004016108ab9190614c61565b60408051608081019091528154815260018201546116c991908390602083019060ff16600381111561168b5761168b614321565b600381111561169c5761169c614321565b81526001919091015460ff6101008204811660208401526201000090910416151560409091015283611d64565b6116d960608301604084016143e6565b6001600160a01b0316336001600160a01b03161415801561171b575061170560808301606084016143e6565b6001600160a01b0316336001600160a01b031614155b1561173c57600c60405163b752672760e01b81526004016108ab9190614c61565b600181015462010000900460ff166117a9576001810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff166201000017905560405183907f154493f7c85d1b6f56279fa2373dd700cca150e26cc77696cf4bf6840079695890600090a25b505050565b6117b6611ddf565b6117c08282612dca565b604080516001600160a01b038416815261ffff831660208201527f6351029c9f321f0ea5d171177c9e81197148bb89784ac71e412efde9451f6abb91015b60405180910390a15050565b611812611ddf565b61181e84848484612e78565b7f5b5d9869885a357cb7fb42f5ebea638e871a9ef2deaccd52701ce05b163e74ba84848484604051610a8b9493929190615379565b61185b611ddf565b610ea181611a3e565b61186c611ddf565b6001600160a01b0381166118e85760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016108ab565b610ea181612cbf565b6118f9611cb8565b6119038282612f3e565b33611916610120840161010085016143e6565b6001600160a01b03161461194057601260405163b752672760e01b81526004016108ab9190614c61565b611960611955610120840161010085016143e6565b8361014001356114a4565b1561198157600160405163b752672760e01b81526004016108ab9190614c61565b6119d4611996610120840161010085016143e6565b8361014001356001600160a01b038216600090815261010260209081526040808320600885901c845290915290208054600160ff84161b1790555050565b7f30f76d5c6d74a15a393da73ac7b48942b55a90319e9355df7b3fef87ae9b272c826040516117fe919061540c565b60006203d090611a12836130c5565b611a1b856130c5565b611a25919061544e565b611a2f908261544e565b9050611365620249f08261544e565b6001600160a01b038116611a6857600060405163b752672760e01b81526004016108ab9190614c61565b60fd80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b600054610100900460ff16611b1f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b6113906131b6565b600054610100900460ff16611ba45760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b61139061323c565b600054610100900460ff16611c295760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b6113906132e3565b600054610100900460ff16611cae5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b610e668282613360565b60975460ff16156113905760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a207061757365640000000000000000000000000000000060448201526064016108ab565b600260655403611d5d5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016108ab565b6002606555565b611db5611d718280614c7b565b611d7a90615141565b611d876020840184614c7b565b611d9090615141565b611da060808501606086016143e6565b611db060608601604087016143e6565b613413565b825114610e6657601160405163b752672760e01b81526004016108ab9190614c61565b6001606555565b6033546001600160a01b031633146113905760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016108ab565b6001600160a01b038116611e6357600060405163b752672760e01b81526004016108ab9190614c61565b60fe80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b60fd546001600160a01b0316336001600160a01b03161461139057600860405163b752672760e01b81526004016108ab9190614c61565b6001600160a01b038084166000908152610100602090815260408083205461ffff878116855261010190935292205491169116811580611f1b57506001600160a01b038116155b80611f385750846001600160a01b0316816001600160a01b031614155b15611f5957600560405163b752672760e01b81526004016108ab9190614c61565b6000838152610105602052604090205460ff1615611f8d57600760405163b752672760e01b81526004016108ab9190614c61565b611fcb8360009081526101056020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b5050505050565b60008080611fe084826134db565b90508060ff16600181111561136557611365614321565b611fff6141ac565b60008061200c84826134db565b905060008160ff16600181111561202557612025614321565b905061203260018461544e565b925060006120408685613541565b905061204d60208561544e565b935060008061205c88876135a7565b915091508095506000806120708a896135a7565b9850915087905060006120838b8361375c565b905061209060148a61544e565b9850600061209e8c8b61375c565b90506120ab60148b61544e565b995060006120b98d8c6137d2565b90506120c660028c61544e565b9a5060006120d48e8d6137d2565b90506120e160028d61544e565b9b506040518061010001604052808b600181111561210157612101614321565b81526020018a8152602001898152602001878152602001856001600160a01b03168152602001846001600160a01b031681526020018361ffff1681526020018261ffff168152509c50505050505050505050505050919050565b6060612165614212565b600080612170614212565b60fe5460e087015161ffff9081167401000000000000000000000000000000000000000090920416146121c7576121b6866020015160018860c001518960e00151613838565b9450925060029150600090506123b3565b60006121d68760400151610749565b905060006121e78860600151610749565b90508180156121f35750805b61222557612210886020015160018a60c001518b60e00151613838565b836002600096509650965096505050506123b3565b60405180610100016040528089602001518152602001896040015181526020018960600151815260200189608001516001600160a01b0316815260200160038081111561227457612274614321565b815260a08a01516001600160a01b0390811660208084019190915260006040808501829052606094850182905260ff54815180870183528381528085018490528083018490528251968701835283875293860183905285820192909252517f6195687300000000000000000000000000000000000000000000000000000000815294975090911692636195687392612313928892909190600401615466565b6020604051808303816000875af192505050801561234e575060408051601f3d908101601f1916820190925261234b91810190614dc0565b60015b6123805761236b886020015160018a60c001518b60e00151613838565b836002600196509650965096505050506123b3565b5061238a836129ba565b6123a3886020015160008a60c001518b60e00151613838565b9650919450600393506001925050505b9193509193565b60fd5461ffff8316600090815261010160205260408082205490517f4b5ca6f4000000000000000000000000000000000000000000000000000000008152919283926001600160a01b0391821692634b5ca6f492349261242d928a9216908b908890620249f09085908d906004016154d8565b60206040518083038185885af115801561244b573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906124709190615532565b95945050505050565b6040805160a081018252600080825260208201819052918101829052606081018290526080810182905290806124af84826134db565b905060008160ff1660018111156124c8576124c8614321565b90506124d560018461544e565b925060006124e38685613541565b90506124f060208561544e565b935060006124fe87866134db565b905060008160ff16600181111561251757612517614321565b905061252460018761544e565b9550600061253289886137d2565b905061253f60028861544e565b9650600061254d8a896137d2565b90506040518060a0016040528087600181111561256c5761256c614321565b815260200186815260200184600181111561258957612589614321565b815261ffff93841660208201529190921660409091015298975050505050505050565b60fe54606082015161ffff9081167401000000000000000000000000000000000000000090920416146125f557600560405163b752672760e01b81526004016108ab9190614c61565b60008160400151600181111561260d5761260d614321565b1461261957600261261c565b60035b6020808301516000908152610104909152604090206001908101805490917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009091169083600381111561267157612671614321565b021790555060208101517f6e41e7188989326595dcc3c42a95c7e931b5ceeadfca132ca83737c4807abeee6000836040015160018111156126b4576126b4614321565b146126c05760026126c3565b60035b6040516126d0919061536c565b60405180910390a250565b6126e58484612f3e565b6126ef8282613893565b60fe546001600160a01b031661270b60608401604085016143e6565b6001600160a01b03161461273557601060405163b752672760e01b81526004016108ab9190614c61565b61275561274a610120860161010087016143e6565b8561014001356114a4565b1561277657600160405163b752672760e01b81526004016108ab9190614c61565b6127838260200135610e6a565b156127a457600260405163b752672760e01b81526004016108ab9190614c61565b4284610120013510156127cd57600360405163b752672760e01b81526004016108ab9190614c61565b60006127e1610180860161016087016143e6565b90506001600160a01b038116158061280157506001600160a01b03811633145b61282157600460405163b752672760e01b81526004016108ab9190614c61565b60fe5474010000000000000000000000000000000000000000900461ffff16610180860135141580612873575061ffff6101a086013516600090815261010160205260409020546001600160a01b0316155b1561289457600560405163b752672760e01b81526004016108ab9190614c61565b60006128b66128a3878061510d565b6128ad9080614c7b565b6102d990615141565b905060006128c76128a3888061510d565b90508180156128d35750805b610f7c57600660405163b752672760e01b81526004016108ab9190614c61565b60008061290483600001518261397b565b905061291483602001518261397b565b608084015160a085015160c086015160e08701516101008801516101208901516040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606097881b81166020830152603482019690965260548101949094529190941b9092166074820152608881019290925260a882015260c8810182905290915060e8015b60408051601f1981840301815291905280516020909101209392505050565b600060fd60070160008360000151815260200190815260200160002090506129f4826020015183604001518460a001518560600151613413565b815560808201516001808301805490917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090911690836003811115612a3b57612a3b614321565b021790555060c0909101516001909101805460ff909216610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff909216919091179055565b606081600001518260200151612a9b84604001516139d3565b612aa885606001516139d3565b85608001518660a001518760c001518860e00151604051602001612ad398979695949392919061556c565b6040516020818303038152906040529050919050565b600080612af68888611a03565b60fd546040517fc23ee3c300000000000000000000000000000000000000000000000000000000815261ffff8816600482015260248101869052604481018390529192506000916001600160a01b039091169063c23ee3c3906064016040805180830381865afa158015612b6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b92919061562f565b50905034811115612bb957600f60405163b752672760e01b81526004016108ab9190614c61565b60fd60000160009054906101000a90046001600160a01b03166001600160a01b0316634b5ca6f434888a8e89888c336040518963ffffffff1660e01b8152600401612c0a97969594939291906154d8565b60206040518083038185885af1158015612c28573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612c4d9190615532565b9a9950505050505050505050565b6001600160a01b038116612c8557600060405163b752672760e01b81526004016108ab9190614c61565b60ff80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b606060cb8054612d3890615653565b80601f0160208091040260200160405190810160405280929190818152602001828054612d6490615653565b8015612db15780601f10612d8657610100808354040283529160200191612db1565b820191906000526020600020905b815481529060010190602001808311612d9457829003601f168201915b5050505050905090565b606060cc8054612d3890615653565b6001600160a01b038216612df457600060405163b752672760e01b81526004016108ab9190614c61565b6001600160a01b03909116600081815261010060209081526040808320805461ffff9096167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009096168617905593825261010190529190912080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055565b60005b83811015611fcb57828282818110612e9557612e95614c32565b9050602002016020810190612eaa91906156a0565b6101066000878785818110612ec157612ec1614c32565b9050602002016020810190612ed691906143e6565b6001600160a01b03168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836003811115612f2757612f27614321565b021790555080612f36816156bb565b915050612e7b565b60007f0000000000000000000000000000000000000000000000000000000000000000612f73612f6e858061510d565b613acb565b612f83612f6e602087018761510d565b612f8f86604001613b30565b612f9b8760a001613b30565b612fad61012089016101008a016143e6565b6101208901356101408a0135612fcb6101808c016101608d016143e6565b60408051602081019a909a528901979097526060880195909552608087019390935260a08601919091526001600160a01b0390811660c086015260e085019190915261010084019190915216610120820152610180808501356101408301526101a08501356101608301520160405160208183030381529060405280519060200120905060006130648361305e84613ba3565b90613beb565b90506001600160a01b038116613082610120860161010087016143e6565b6001600160a01b031614610a945760006040517fb6cd316c0000000000000000000000000000000000000000000000000000000081526004016108ab91906156f3565b600080806130d38480615700565b9050905060005b818110156131ad5760026130ee8680615700565b838181106130fe576130fe614c32565b905060800201602001602081019061311691906156a0565b600381111561312757613127614321565b0361314057613139620138808461544e565b92506131a5565b600161314c8680615700565b8381811061315c5761315c614c32565b905060800201602001602081019061317491906156a0565b600381111561318557613185614321565b036131965761313961ea608461544e565b6131a261ea608461544e565b92505b6001016130da565b50909392505050565b600054610100900460ff166132335760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b61139033612cbf565b600054610100900460ff166132b95760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b609780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b600054610100900460ff16611dd85760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b600054610100900460ff166133dd5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b81516133f09060cb906020850190614278565b5080516134049060cc906020840190614278565b5050600060c981905560ca5550565b60008061341f866139d3565b81604051602001613431929190615768565b604051602081830303815290604052805190602001209050613452856139d3565b81604051602001613464929190615768565b60408051601f1981840301815282825280516020918201207fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606098891b8116838601529690971b909516603483015260488083019690965280518083039096018652606890910190525050815191012092915050565b60006134e882600161544e565b835110156135385760405162461bcd60e51b815260206004820152601360248201527f746f55696e74385f6f75744f66426f756e64730000000000000000000000000060448201526064016108ab565b50016001015190565b600061354e82602061544e565b8351101561359e5760405162461bcd60e51b815260206004820152601560248201527f746f427974657333325f6f75744f66426f756e6473000000000000000000000060448201526064016108ab565b50016020015190565b6040805160208101909152606081526000806135c38585613c0f565b90506135d060208561544e565b935060008167ffffffffffffffff8111156135ed576135ed614401565b60405190808252806020026020018201604052801561364a57816020015b613637604080516080810190915260008082526020820190815260200160008152602001600081525090565b81526020019060019003908161360b5790505b50905060005b8281101561373f576000613664888861375c565b905061367160148861544e565b9650600061367f89896134db565b905061368c60018961544e565b9750600061369a8a8a613c0f565b90506136a760208a61544e565b985060006136b58b8b613c0f565b90506136c260208b61544e565b99506040518060800160405280856001600160a01b031681526020018460ff1660038111156136f3576136f3614321565b600381111561370457613704614321565b81526020018381526020018281525086868151811061372557613725614c32565b602002602001018190525084600101945050505050613650565b5060408051602081019091529081529250839150505b9250929050565b600061376982601461544e565b835110156137b95760405162461bcd60e51b815260206004820152601560248201527f746f416464726573735f6f75744f66426f756e6473000000000000000000000060448201526064016108ab565b5001602001516c01000000000000000000000000900490565b60006137df82600261544e565b8351101561382f5760405162461bcd60e51b815260206004820152601460248201527f746f55696e7431365f6f75744f66426f756e647300000000000000000000000060448201526064016108ab565b50016002015190565b60606124706040518060a0016040528060018081111561385a5761385a614321565b815260200187815260200186600181111561387757613877614321565b81526020018561ffff1681526020018461ffff16815250613c6c565b60007f0000000000000000000000000000000000000000000000000000000000000000833560208501356138cd60608701604088016143e6565b60408051602081019590955284019290925260608301526001600160a01b0316608082015260a001604051602081830303815290604052805190602001209050600061391c8361305e84613ba3565b90506001600160a01b03811661393860608601604087016143e6565b6001600160a01b031614610a945760016040517fb6cd316c0000000000000000000000000000000000000000000000000000000081526004016108ab91906156f3565b600061398a83600001516139d3565b8260405160200161399c929190615768565b6040516020818303038152906040528051906020012091506139c183602001516139d3565b8260405160200161299b929190615768565b805151606090819060005b81811015613a9d5782856000015182815181106139fd576139fd614c32565b60200260200101516000015186600001518381518110613a1f57613a1f614c32565b60200260200101516020015187600001518481518110613a4157613a41614c32565b60200260200101516040015188600001518581518110613a6357613a63614c32565b602002602001015160600151604051602001613a8395949392919061578a565b60408051601f1981840301815291905292506001016139de565b50835151604051613ab3919084906020016157f5565b60405160208183030381529060405292505050919050565b6000807f0000000000000000000000000000000000000000000000000000000000000000613b01613afc8580614c7b565b613c9b565b613b11613afc6020870187614c7b565b604080516020810194909452830191909152606082015260800161299b565b6000807f0000000000000000000000000000000000000000000000000000000000000000613b6160208501856143e6565b613b7160408601602087016143e6565b6040805160208101949094526001600160a01b039283168482015291166060830152840135608082015260a00161299b565b6000610e8a613bb0613dd1565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6000806000613bfa8585613de0565b91509150613c0781613e22565b509392505050565b6000613c1c82602061544e565b8351101561359e5760405162461bcd60e51b815260206004820152601560248201527f746f55696e743235365f6f75744f66426f756e6473000000000000000000000060448201526064016108ab565b606081600001518260200151836040015184606001518560800151604051602001612ad395949392919061581b565b600080613ca88380615700565b9050905060008167ffffffffffffffff811115613cc757613cc7614401565b604051908082528060200260200182016040528015613cf0578160200160208202803683370190505b50905060005b82811015613d4b57613d26613d0b8680615700565b83818110613d1b57613d1b614c32565b905060800201613f87565b828281518110613d3857613d38614c32565b6020908102919091010152600101613cf6565b5060007f000000000000000000000000000000000000000000000000000000000000000082604051602001613d809190615882565b60405160208183030381529060405280519060200120604051602001613db0929190918252602082015260400190565b60408051601f19818403018152919052805160209091012095945050505050565b6000613ddb613fe6565b905090565b6000808251604103613e165760208301516040840151606085015160001a613e0a8782858561405a565b94509450505050613755565b50600090506002613755565b6000816004811115613e3657613e36614321565b03613e3e5750565b6001816004811115613e5257613e52614321565b03613e9f5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016108ab565b6002816004811115613eb357613eb3614321565b03613f005760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016108ab565b6003816004811115613f1457613f14614321565b03610ea15760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f756500000000000000000000000000000000000000000000000000000000000060648201526084016108ab565b6000807f0000000000000000000000000000000000000000000000000000000000000000613fb860208501856143e6565b613fc860408601602087016156a0565b8560400135866060013560405160200161299b9594939291906158b8565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f61401161411e565b61401961417b565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156140915750600090506003614115565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156140e5573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661410e57600060019250925050614115565b9150600090505b94509492505050565b600080614129612d29565b805190915015614140578051602090910120919050565b60c954801561414f5792915050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4709250505090565b5090565b600080614186612dbb565b80519091501561419d578051602090910120919050565b60ca54801561414f5792915050565b604080516101008101825260008082526020808301919091528251908101835260608152909182019081526020016141f06040518060200160405280606081525090565b8152600060208201819052604082018190526060820181905260809091015290565b60408051610100810182526000815281516020818101909352606081529091820190815260200161424f6040518060200160405280606081525090565b815260006020820181905260408201819052606082018190526080820181905260a09091015290565b82805461428490615653565b90600052602060002090601f0160209004810192826142a657600085556142ec565b82601f106142bf57805160ff19168380011785556142ec565b828001600101855582156142ec579182015b828111156142ec5782518255916020019190600101906142d1565b506141779291505b8082111561417757600081556001016142f4565b60006020828403121561431a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110610ea157610ea1614321565b815181526020820151608082019061437781614350565b8060208401525060ff604084015116604083015260608301511515606083015292915050565b803561ffff811681146143af57600080fd5b919050565b6000602082840312156143c657600080fd5b6114d88261439d565b80356001600160a01b03811681146143af57600080fd5b6000602082840312156143f857600080fd5b6114d8826143cf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516020810167ffffffffffffffff8111828210171561445357614453614401565b60405290565b6040516080810167ffffffffffffffff8111828210171561445357614453614401565b604051610140810167ffffffffffffffff8111828210171561445357614453614401565b604051601f8201601f1916810167ffffffffffffffff811182821017156144c9576144c9614401565b604052919050565b600067ffffffffffffffff8211156144eb576144eb614401565b5060051b60200190565b8035600481106143af57600080fd5b6000602080838503121561451757600080fd5b61451f614430565b9150823567ffffffffffffffff81111561453857600080fd5b8301601f8101851361454957600080fd5b803561455c614557826144d1565b6144a0565b81815260079190911b8201830190838101908783111561457b57600080fd5b928401925b828410156145e057608084890312156145995760008081fd5b6145a1614459565b6145aa856143cf565b81526145b78686016144f5565b818701526040858101359082015260608086013590820152825260809093019290840190614580565b8552509295945050505050565b6000602082840312156145ff57600080fd5b813567ffffffffffffffff81111561461657600080fd5b61136584828501614504565b803560ff811681146143af57600080fd5b60008060006060848603121561464857600080fd5b614651846143cf565b925061465f6020850161439d565b915061466d60408501614622565b90509250925092565b6000806040838503121561468957600080fd5b82359150602083013567ffffffffffffffff8111156146a757600080fd5b8301608081860312156146b957600080fd5b809150509250929050565b600082601f8301126146d557600080fd5b813567ffffffffffffffff8111156146ef576146ef614401565b6147026020601f19601f840116016144a0565b81815284602083860101111561471757600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a0868803121561474c57600080fd5b853567ffffffffffffffff8082111561476457600080fd5b61477089838a016146c4565b965060209150818801358181111561478757600080fd5b8801601f81018a1361479857600080fd5b80356147a6614557826144d1565b81815260059190911b8201840190848101908c8311156147c557600080fd5b8584015b838110156147fd578035868111156147e15760008081fd5b6147ef8f89838901016146c4565b8452509186019186016147c9565b509850505050604088013594506148199150506060870161439d565b949793965091946080013592915050565b60006020828403121561483c57600080fd5b6114d882614622565b60006101c0828403121561485857600080fd5b50919050565b60008060008084860360c081121561487557600080fd5b853567ffffffffffffffff8082111561488d57600080fd5b61489989838a01614845565b965060208801359150808211156148af57600080fd5b6148bb89838a016146c4565b955060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0840112156148ed57600080fd5b60408801945060a088013592508083111561490757600080fd5b5050614915878288016146c4565b91505092959194509250565b60005b8381101561493c578181015183820152602001614924565b83811115610a945750506000910152565b60008151808452614965816020860160208601614921565b601f01601f19169290920160200192915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e0818401526149b560e084018a61494d565b83810360408501526149c7818a61494d565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b81811015614a19578351835292840192918401916001016149fd565b50909c9b505050505050505050505050565b60008060408385031215614a3e57600080fd5b614a47836143cf565b946020939093013593505050565b60008060408385031215614a6857600080fd5b823591506020830135600281106146b957600080fd5b60008060408385031215614a9157600080fd5b614a9a836143cf565b9150614aa86020840161439d565b90509250929050565b60008083601f840112614ac357600080fd5b50813567ffffffffffffffff811115614adb57600080fd5b6020830191508360208260051b850101111561375557600080fd5b60008060008060408587031215614b0c57600080fd5b843567ffffffffffffffff80821115614b2457600080fd5b614b3088838901614ab1565b90965094506020870135915080821115614b4957600080fd5b50614b5687828801614ab1565b95989497509550505050565b60008060408385031215614b7557600080fd5b823567ffffffffffffffff80821115614b8d57600080fd5b614b9986838701614845565b93506020850135915080821115614baf57600080fd5b50614bbc858286016146c4565b9150509250929050565b60006020828403121561485857600080fd5b60008060408385031215614beb57600080fd5b823567ffffffffffffffff80821115614c0357600080fd5b614c0f86838701614bc6565b93506020850135915080821115614c2557600080fd5b50614bbc85828601614bc6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020810160138310614c7557614c75614321565b91905290565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1833603018112614caf57600080fd5b9190910192915050565b6000602080840183357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1853603018112614cf257600080fd5b8401803567ffffffffffffffff811115614d0b57600080fd5b8060071b3603861315614d1d57600080fd5b838752918290526040919083019082870160005b82811015614d89576001600160a01b03614d4a856143cf565b168252614d588685016144f5565b614d6181614350565b8287015283850135858301526060808501359083015260809384019390910190600101614d31565b50979650505050505050565b604081526000614da86040830185614cb9565b90506001600160a01b03831660208301529392505050565b600060208284031215614dd257600080fd5b815180151581146114d857600080fd5b8051602080845281518482018190526000926040919083019082870190855b81811015614e4d5783516001600160a01b03815116845286810151614e2581614350565b8488015280860151868501526060908101519084015292850192608090920191600101614e01565b5090979650505050505050565b6000610100825184526020830151816020860152614e7a82860182614de2565b91505060408301518482036040860152614e948282614de2565b91505060608301516001600160a01b03808216606087015260808501519150614ebc82614350565b8160808701528060a08601511660a0870152505060c0830151614ee460c086018260ff169052565b5060e0830151613c0760e086018215159052565b606081526000614f0b6060830186614e5a565b9050614f1684614350565b83602083015267ffffffffffffffff83166040830152949350505050565b600060408284031215614f4657600080fd5b6040516040810167ffffffffffffffff8282108183111715614f6a57614f6a614401565b816040528293508435915080821115614f8257600080fd5b614f8e86838701614504565b83526020850135915080821115614fa457600080fd5b50614fb185828601614504565b6020830152505092915050565b600060608284031215614fd057600080fd5b6040516060810181811067ffffffffffffffff82111715614ff357614ff3614401565b604052905080615002836143cf565b8152615010602084016143cf565b6020820152604083013560408201525092915050565b60006101c0823603121561503957600080fd5b61504161447c565b823567ffffffffffffffff8082111561505957600080fd5b61506536838701614f34565b8352602085013591508082111561507b57600080fd5b5061508836828601614f34565b60208301525061509b3660408501614fbe565b60408201526150ad3660a08501614fbe565b60608201526101006150c08185016143cf565b60808301526101208085013560a084015261014085013560c08401526150e961016086016143cf565b60e0840152610180850135828401526101a085013581840152505080915050919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112614caf57600080fd5b6000610e8a3683614504565b6001600160a01b038061515f836143cf565b1683528061516f602084016143cf565b16602084015250604090810135910152565b60e08152600061519460e0830186614e5a565b90506151a3602083018561514d565b611365608083018461514d565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126151e457600080fd5b90910192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18336030181126151e457600080fd5b600061522d82836151ed565b6040845261523e6040850182614cb9565b905061524d60208401846151ed565b84820360208601526124708282614cb9565b60006101c061526e83846151b0565b81855261527d82860182615221565b91505061528d60208401846151b0565b848203602086015261529f8282615221565b9150506152b2604085016040850161514d565b6152c260a0850160a0850161514d565b6101006152d08185016143cf565b6001600160a01b031690850152610120838101359085015261014080840135908501526101606153018185016143cf565b6001600160a01b03169085015261018083810135908501526101a09283013592909301919091525090565b60608152600061533f606083018661525f565b82810360208401526153518186614e5a565b91505067ffffffffffffffff83166040830152949350505050565b60208101614c7583614350565b6040808252810184905260008560608301825b878110156153ba576001600160a01b036153a5846143cf565b1682526020928301929091019060010161538c565b5083810360208581019190915285825291508590820160005b868110156153ff576153e4836144f5565b6153ed81614350565b825291830191908301906001016153d3565b5098975050505050505050565b6020815260006114d8602083018461525f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156154615761546161541f565b500190565b60e08152600061547960e0830186614e5a565b90506154ac60208301856001600160a01b0380825116835280602083015116602084015250604081015160408301525050565b82516001600160a01b03908116608084015260208401511660a0830152604083015160c0830152611365565b600061ffff808a1683526001600160a01b03808a16602085015260e0604085015261550660e085018a61494d565b925087606085015286608085015281861660a085015280851660c0850152505098975050505050505050565b60006020828403121561554457600080fd5b815167ffffffffffffffff811681146114d857600080fd5b60028110610ea157610ea1614321565b6155758961555c565b8860f81b815287600182015260008751615596816021850160208c01614921565b8751908301906155ad816021840160208c01614921565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606098891b81169190920160218101919091529590961b9095166035850152507fffff00000000000000000000000000000000000000000000000000000000000060f092831b81166049850152911b16604b820152604d0195945050505050565b6000806040838503121561564257600080fd5b505080516020909101519092909150565b600181811c9082168061566757607f821691505b602082108103614858577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602082840312156156b257600080fd5b6114d8826144f5565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036156ec576156ec61541f565b5060010190565b60208101614c758361555c565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261573557600080fd5b83018035915067ffffffffffffffff82111561575057600080fd5b6020019150600781901b360382131561375557600080fd5b6000835161577a818460208801614921565b9190910191825250602001919050565b6000865161579c818460208b01614921565b80830190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b1681526157d286614350565b60f89590951b601486015250506015830191909152603582015260550192915050565b8281526000825161580d816020850160208701614921565b919091016020019392505050565b6158248661555c565b8560f81b81528460018201526158398461555c565b60f89390931b60218401527fffff00000000000000000000000000000000000000000000000000000000000060f092831b81166022850152911b16602482015260260192915050565b815160009082906020808601845b838110156158ac57815185529382019390820190600101615890565b50929695505050505050565b8581526001600160a01b038516602082015260a081016158d785614350565b846040830152836060830152826080830152969550505050505056fea2646970667358221220c844c17fae3010e0a0774537478dfd129b230e3b6cd52b5ce75c673ba83435c764736f6c634300080d0033

Deployed Bytecode

0x6080604052600436106101e25760003560e01c8063751a0c6411610102578063cf40827d11610095578063e8dfd50811610064578063e8dfd508146105fe578063f2fde38b1461063b578063f7503fc41461065b578063f763c1fc1461067b57600080fd5b8063cf40827d14610580578063d61bd89a146105a0578063da25b725146105c0578063e159378d146105de57600080fd5b80638da5cb5b116100d15780638da5cb5b146104f157806392e08a6b1461050f5780639c2aa8691461052f578063a87f32791461054f57600080fd5b8063751a0c6414610458578063793e64e31461047857806384b0196e146104a957806389535803146104d157600080fd5b806351ab15751161017a5780635e8345b3116101495780635e8345b3146103d95780635ffaf562146104055780636817031b14610423578063715018a61461044357600080fd5b806351ab15751461036e578063529dca321461038e578063538ee295146103a15780635c975abb146103c157600080fd5b80633990264e116101b65780633990264e146102ee578063430bf08a146103105780634a7d85171461032e5780634f5b96c71461034e57600080fd5b8062162420146101e757806309dfa5721461021d57806323602e5a146102705780632fbb792b146102be575b600080fd5b3480156101f357600080fd5b50610207610202366004614308565b6106a9565b6040516102149190614360565b60405180910390f35b34801561022957600080fd5b506102586102383660046143b4565b61ffff16600090815261010160205260409020546001600160a01b031690565b6040516001600160a01b039091168152602001610214565b34801561027c57600080fd5b506102ab61028b3660046143e6565b6001600160a01b03166000908152610100602052604090205461ffff1690565b60405161ffff9091168152602001610214565b3480156102ca57600080fd5b506102de6102d93660046145ed565b610749565b6040519015158152602001610214565b3480156102fa57600080fd5b5061030e610309366004614633565b610803565b005b34801561031c57600080fd5b5060ff546001600160a01b0316610258565b34801561033a57600080fd5b5061030e610349366004614676565b610a9a565b34801561035a57600080fd5b506102de610369366004614308565b610e6a565b34801561037a57600080fd5b5061030e6103893660046143e6565b610e90565b61030e61039c366004614734565b610ea4565b3480156103ad57600080fd5b5061030e6103bc36600461482a565b610f86565b3480156103cd57600080fd5b5060975460ff166102de565b6103ec6103e736600461485e565b610fd5565b60405167ffffffffffffffff9091168152602001610214565b34801561041157600080fd5b5060fe546001600160a01b0316610258565b34801561042f57600080fd5b5061030e61043e3660046143e6565b61136d565b34801561044f57600080fd5b5061030e61137e565b34801561046457600080fd5b5061030e6104733660046143b4565b611392565b34801561048457600080fd5b5060fe5474010000000000000000000000000000000000000000900461ffff166102ab565b3480156104b557600080fd5b506104be6113e2565b6040516102149796959493929190614979565b3480156104dd57600080fd5b506102de6104ec366004614a2b565b6114a4565b3480156104fd57600080fd5b506033546001600160a01b0316610258565b34801561051b57600080fd5b5061030e61052a366004614a55565b6114df565b34801561053b57600080fd5b5061030e61054a366004614676565b61160d565b34801561055b57600080fd5b506102de61056a366004614308565b6000908152610105602052604090205460ff1690565b34801561058c57600080fd5b5061030e61059b366004614a7e565b6117ae565b3480156105ac57600080fd5b5061030e6105bb366004614af6565b61180a565b3480156105cc57600080fd5b5060fd546001600160a01b0316610258565b3480156105ea57600080fd5b5061030e6105f93660046143e6565b611853565b34801561060a57600080fd5b5060fd5474010000000000000000000000000000000000000000900460ff1660405160ff9091168152602001610214565b34801561064757600080fd5b5061030e6106563660046143e6565b611864565b34801561066757600080fd5b5061030e610676366004614b62565b6118f1565b34801561068757600080fd5b5061069b610696366004614bd8565b611a03565b604051908152602001610214565b60408051608081018252600080825260208201819052918101829052606081019190915260008281526101046020908152604091829020825160808101909352805483526001810154909183019060ff16600381111561070b5761070b614321565b600381111561071c5761071c614321565b81526001919091015460ff6101008204811660208401526201000090910416151560409091015292915050565b80515160009081905b808210156107f957835180518390811061076e5761076e614c32565b602002602001015160200151600381111561078b5761078b614321565b8451805161010691600091869081106107a6576107a6614c32565b602090810291909101810151516001600160a01b031682528101919091526040016000205460ff1660038111156107df576107df614321565b146107ee575060009392505050565b816001019150610752565b5060019392505050565b600054610100900460ff16158080156108235750600054600160ff909116105b8061083d5750303b15801561083d575060005460ff166001145b6108b45760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561091257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b61091b84611a3e565b60fd80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff85160217905560fe80547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000061ffff8616021790556109ac611aa2565b6109b4611b27565b6109bc611bac565b610a306040518060400160405280601481526020017f4e46332043726f7373636861696e2053776170730000000000000000000000008152506040518060400160405280600581526020017f302e322e30000000000000000000000000000000000000000000000000000000815250611c31565b8015610a9457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15b50505050565b610aa2611cb8565b610aaa611d0b565b600082815261010460209081526040808320815160808101909252805482526001810154919290919083019060ff166003811115610aea57610aea614321565b6003811115610afb57610afb614321565b81526001919091015460ff610100820481166020840152620100009091041615156040909101529050600381602001516003811115610b3c57610b3c614321565b14158015610b605750600281602001516003811115610b5d57610b5d614321565b14155b15610b8157600960405163b752672760e01b81526004016108ab9190614c61565b610b8b8183611d64565b610b9b60808301606084016143e6565b6001600160a01b03163303610cdd57604081015160021615610bd357600a60405163b752672760e01b81526004016108ab9190614c61565b60ff546001600160a01b031663d7f5dcf2600383602001516003811115610bfc57610bfc614321565b14610c1357610c0e6020850185614c7b565b610c1d565b610c1d8480614c7b565b610c2d60808601606087016143e6565b6040518363ffffffff1660e01b8152600401610c4a929190614d95565b6020604051808303816000875af1158015610c69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c8d9190614dc0565b5060008381526101046020526040902060010180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff811660026101009283900460ff1617909102179055610e2e565b610ced60608301604084016143e6565b6001600160a01b03163303610e2e57604081015160011615610d2557600b60405163b752672760e01b81526004016108ab9190614c61565b60ff546001600160a01b031663d7f5dcf2600383602001516003811115610d4e57610d4e614321565b14610d6257610d5d8480614c7b565b610d6f565b610d6f6020850185614c7b565b610d7f60608601604087016143e6565b6040518363ffffffff1660e01b8152600401610d9c929190614d95565b6020604051808303816000875af1158015610dbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ddf9190614dc0565b50600083815261010460205260409020600190810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff81166101009182900460ff16909317029190911790555b604051339084907f277b97242b69f3f0ed095a3b632f6929f0d4edf01019ccd1ab48db0b4f5ef0b290600090a350610e666001606555565b5050565b600881901c60009081526101036020526040812054600160ff84161b1615155b92915050565b610e98611ddf565b610ea181611e39565b50565b610eac611e9d565b610eb7838383611ed4565b6000610ec286611fd2565b90506000816001811115610ed857610ed8614321565b03610f66576000610ee887611ff7565b9050600080600080610ef98561215b565b93509350935093506000610f12858a8860a001516123ba565b90508115610f5b5785602001517f133006c2d7c0bc372186e62dd70325ca161419a7c99165a403d04f29eace5c17858584604051610f5293929190614ef8565b60405180910390a25b505050505050610f7e565b6000610f7187612479565b9050610f7c816125ac565b505b505050505050565b610f8e611ddf565b60fd80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff84160217905550565b6000610fdf611cb8565b610fe7611d0b565b610ff3858585856126db565b600061100661100187615026565b6128f3565b60408051610100810190915281815290915060009060208101611029898061510d565b6110339080614c7b565b61103c90615141565b8152602001888060200190611051919061510d565b61105b9080614c7b565b61106490615141565b815260200161107b6101208a016101008b016143e6565b6001600160a01b0316815260200160018152602001336001600160a01b03908116825260006020830181905260409283015260ff5482517f6195687300000000000000000000000000000000000000000000000000000000815293945016916361956873916110f69185918c019060a08d0190600401615181565b6020604051808303816000875af1158015611115573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111399190614dc0565b5061118d61114f61012089016101008a016143e6565b8861014001356001600160a01b038216600090815261010260209081526040808320600885901c845290915290208054600160ff84161b1790555050565b6111b88560200135600881901c6000908152610103602052604090208054600160ff84161b17905550565b6111c1816129ba565b600061128a604051806101000160405280600060018111156111e5576111e5614321565b8152602081018690526040016111fb8b8061510d565b611209906020810190614c7b565b61121290615141565b81526020018a8060200190611227919061510d565b611235906020810190614c7b565b61123e90615141565b815260200184606001516001600160a01b031681526020018460a001516001600160a01b031681526020018a610180013561ffff1681526020018a6101a0013561ffff16815250612a82565b90506112f58161129a8a8061510d565b6112a8906020810190614c7b565b6112b560208c018c61510d565b6112c3906020810190614c7b565b6101a08c013561ffff8116600090815261010160205260409020546001600160a01b0316906101808e01358c35612ae9565b935061130961012089016101008a016143e6565b6001600160a01b0316336001600160a01b0316847fb31aaaa4ab267f40fb7e7a643dc5aabab8ac321fc7010469bf681f2970a9a0638b86896040516113509392919061532c565b60405180910390a45050506113656001606555565b949350505050565b611375611ddf565b610ea181612c5b565b611386611ddf565b6113906000612cbf565b565b61139a611ddf565b60fe80547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000061ffff84160217905550565b60006060806000806000606060c9546000801b148015611402575060ca54155b61144e5760405162461bcd60e51b815260206004820152601560248201527f4549503731323a20556e696e697469616c697a6564000000000000000000000060448201526064016108ab565b611456612d29565b61145e612dbb565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b6001600160a01b038216600090815261010260209081526040808320600885901c8452909152812054600160ff84161b1615155b9392505050565b6114e7611ddf565b60008281526101046020526040902060018082015460ff16600381111561151057611510614321565b1461153157600960405163b752672760e01b81526004016108ab9190614c61565b600181015462010000900460ff1661155f57600e60405163b752672760e01b81526004016108ab9190614c61565b600082600181111561157357611573614321565b1461157f576002611582565b60035b6001808301805490917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116908360038111156115c2576115c2614321565b0217905550600181015460405184917f08a6782de64ec46be9fdf6cb9403f0c945f92ca67c1e426388abe40d25dc1000916116009160ff169061536c565b60405180910390a2505050565b60008281526101046020526040902060018082015460ff16600381111561163657611636614321565b1461165757600960405163b752672760e01b81526004016108ab9190614c61565b60408051608081019091528154815260018201546116c991908390602083019060ff16600381111561168b5761168b614321565b600381111561169c5761169c614321565b81526001919091015460ff6101008204811660208401526201000090910416151560409091015283611d64565b6116d960608301604084016143e6565b6001600160a01b0316336001600160a01b03161415801561171b575061170560808301606084016143e6565b6001600160a01b0316336001600160a01b031614155b1561173c57600c60405163b752672760e01b81526004016108ab9190614c61565b600181015462010000900460ff166117a9576001810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff166201000017905560405183907f154493f7c85d1b6f56279fa2373dd700cca150e26cc77696cf4bf6840079695890600090a25b505050565b6117b6611ddf565b6117c08282612dca565b604080516001600160a01b038416815261ffff831660208201527f6351029c9f321f0ea5d171177c9e81197148bb89784ac71e412efde9451f6abb91015b60405180910390a15050565b611812611ddf565b61181e84848484612e78565b7f5b5d9869885a357cb7fb42f5ebea638e871a9ef2deaccd52701ce05b163e74ba84848484604051610a8b9493929190615379565b61185b611ddf565b610ea181611a3e565b61186c611ddf565b6001600160a01b0381166118e85760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016108ab565b610ea181612cbf565b6118f9611cb8565b6119038282612f3e565b33611916610120840161010085016143e6565b6001600160a01b03161461194057601260405163b752672760e01b81526004016108ab9190614c61565b611960611955610120840161010085016143e6565b8361014001356114a4565b1561198157600160405163b752672760e01b81526004016108ab9190614c61565b6119d4611996610120840161010085016143e6565b8361014001356001600160a01b038216600090815261010260209081526040808320600885901c845290915290208054600160ff84161b1790555050565b7f30f76d5c6d74a15a393da73ac7b48942b55a90319e9355df7b3fef87ae9b272c826040516117fe919061540c565b60006203d090611a12836130c5565b611a1b856130c5565b611a25919061544e565b611a2f908261544e565b9050611365620249f08261544e565b6001600160a01b038116611a6857600060405163b752672760e01b81526004016108ab9190614c61565b60fd80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b600054610100900460ff16611b1f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b6113906131b6565b600054610100900460ff16611ba45760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b61139061323c565b600054610100900460ff16611c295760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b6113906132e3565b600054610100900460ff16611cae5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b610e668282613360565b60975460ff16156113905760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a207061757365640000000000000000000000000000000060448201526064016108ab565b600260655403611d5d5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016108ab565b6002606555565b611db5611d718280614c7b565b611d7a90615141565b611d876020840184614c7b565b611d9090615141565b611da060808501606086016143e6565b611db060608601604087016143e6565b613413565b825114610e6657601160405163b752672760e01b81526004016108ab9190614c61565b6001606555565b6033546001600160a01b031633146113905760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016108ab565b6001600160a01b038116611e6357600060405163b752672760e01b81526004016108ab9190614c61565b60fe80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b60fd546001600160a01b0316336001600160a01b03161461139057600860405163b752672760e01b81526004016108ab9190614c61565b6001600160a01b038084166000908152610100602090815260408083205461ffff878116855261010190935292205491169116811580611f1b57506001600160a01b038116155b80611f385750846001600160a01b0316816001600160a01b031614155b15611f5957600560405163b752672760e01b81526004016108ab9190614c61565b6000838152610105602052604090205460ff1615611f8d57600760405163b752672760e01b81526004016108ab9190614c61565b611fcb8360009081526101056020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b5050505050565b60008080611fe084826134db565b90508060ff16600181111561136557611365614321565b611fff6141ac565b60008061200c84826134db565b905060008160ff16600181111561202557612025614321565b905061203260018461544e565b925060006120408685613541565b905061204d60208561544e565b935060008061205c88876135a7565b915091508095506000806120708a896135a7565b9850915087905060006120838b8361375c565b905061209060148a61544e565b9850600061209e8c8b61375c565b90506120ab60148b61544e565b995060006120b98d8c6137d2565b90506120c660028c61544e565b9a5060006120d48e8d6137d2565b90506120e160028d61544e565b9b506040518061010001604052808b600181111561210157612101614321565b81526020018a8152602001898152602001878152602001856001600160a01b03168152602001846001600160a01b031681526020018361ffff1681526020018261ffff168152509c50505050505050505050505050919050565b6060612165614212565b600080612170614212565b60fe5460e087015161ffff9081167401000000000000000000000000000000000000000090920416146121c7576121b6866020015160018860c001518960e00151613838565b9450925060029150600090506123b3565b60006121d68760400151610749565b905060006121e78860600151610749565b90508180156121f35750805b61222557612210886020015160018a60c001518b60e00151613838565b836002600096509650965096505050506123b3565b60405180610100016040528089602001518152602001896040015181526020018960600151815260200189608001516001600160a01b0316815260200160038081111561227457612274614321565b815260a08a01516001600160a01b0390811660208084019190915260006040808501829052606094850182905260ff54815180870183528381528085018490528083018490528251968701835283875293860183905285820192909252517f6195687300000000000000000000000000000000000000000000000000000000815294975090911692636195687392612313928892909190600401615466565b6020604051808303816000875af192505050801561234e575060408051601f3d908101601f1916820190925261234b91810190614dc0565b60015b6123805761236b886020015160018a60c001518b60e00151613838565b836002600196509650965096505050506123b3565b5061238a836129ba565b6123a3886020015160008a60c001518b60e00151613838565b9650919450600393506001925050505b9193509193565b60fd5461ffff8316600090815261010160205260408082205490517f4b5ca6f4000000000000000000000000000000000000000000000000000000008152919283926001600160a01b0391821692634b5ca6f492349261242d928a9216908b908890620249f09085908d906004016154d8565b60206040518083038185885af115801561244b573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906124709190615532565b95945050505050565b6040805160a081018252600080825260208201819052918101829052606081018290526080810182905290806124af84826134db565b905060008160ff1660018111156124c8576124c8614321565b90506124d560018461544e565b925060006124e38685613541565b90506124f060208561544e565b935060006124fe87866134db565b905060008160ff16600181111561251757612517614321565b905061252460018761544e565b9550600061253289886137d2565b905061253f60028861544e565b9650600061254d8a896137d2565b90506040518060a0016040528087600181111561256c5761256c614321565b815260200186815260200184600181111561258957612589614321565b815261ffff93841660208201529190921660409091015298975050505050505050565b60fe54606082015161ffff9081167401000000000000000000000000000000000000000090920416146125f557600560405163b752672760e01b81526004016108ab9190614c61565b60008160400151600181111561260d5761260d614321565b1461261957600261261c565b60035b6020808301516000908152610104909152604090206001908101805490917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009091169083600381111561267157612671614321565b021790555060208101517f6e41e7188989326595dcc3c42a95c7e931b5ceeadfca132ca83737c4807abeee6000836040015160018111156126b4576126b4614321565b146126c05760026126c3565b60035b6040516126d0919061536c565b60405180910390a250565b6126e58484612f3e565b6126ef8282613893565b60fe546001600160a01b031661270b60608401604085016143e6565b6001600160a01b03161461273557601060405163b752672760e01b81526004016108ab9190614c61565b61275561274a610120860161010087016143e6565b8561014001356114a4565b1561277657600160405163b752672760e01b81526004016108ab9190614c61565b6127838260200135610e6a565b156127a457600260405163b752672760e01b81526004016108ab9190614c61565b4284610120013510156127cd57600360405163b752672760e01b81526004016108ab9190614c61565b60006127e1610180860161016087016143e6565b90506001600160a01b038116158061280157506001600160a01b03811633145b61282157600460405163b752672760e01b81526004016108ab9190614c61565b60fe5474010000000000000000000000000000000000000000900461ffff16610180860135141580612873575061ffff6101a086013516600090815261010160205260409020546001600160a01b0316155b1561289457600560405163b752672760e01b81526004016108ab9190614c61565b60006128b66128a3878061510d565b6128ad9080614c7b565b6102d990615141565b905060006128c76128a3888061510d565b90508180156128d35750805b610f7c57600660405163b752672760e01b81526004016108ab9190614c61565b60008061290483600001518261397b565b905061291483602001518261397b565b608084015160a085015160c086015160e08701516101008801516101208901516040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606097881b81166020830152603482019690965260548101949094529190941b9092166074820152608881019290925260a882015260c8810182905290915060e8015b60408051601f1981840301815291905280516020909101209392505050565b600060fd60070160008360000151815260200190815260200160002090506129f4826020015183604001518460a001518560600151613413565b815560808201516001808301805490917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090911690836003811115612a3b57612a3b614321565b021790555060c0909101516001909101805460ff909216610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff909216919091179055565b606081600001518260200151612a9b84604001516139d3565b612aa885606001516139d3565b85608001518660a001518760c001518860e00151604051602001612ad398979695949392919061556c565b6040516020818303038152906040529050919050565b600080612af68888611a03565b60fd546040517fc23ee3c300000000000000000000000000000000000000000000000000000000815261ffff8816600482015260248101869052604481018390529192506000916001600160a01b039091169063c23ee3c3906064016040805180830381865afa158015612b6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b92919061562f565b50905034811115612bb957600f60405163b752672760e01b81526004016108ab9190614c61565b60fd60000160009054906101000a90046001600160a01b03166001600160a01b0316634b5ca6f434888a8e89888c336040518963ffffffff1660e01b8152600401612c0a97969594939291906154d8565b60206040518083038185885af1158015612c28573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612c4d9190615532565b9a9950505050505050505050565b6001600160a01b038116612c8557600060405163b752672760e01b81526004016108ab9190614c61565b60ff80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b606060cb8054612d3890615653565b80601f0160208091040260200160405190810160405280929190818152602001828054612d6490615653565b8015612db15780601f10612d8657610100808354040283529160200191612db1565b820191906000526020600020905b815481529060010190602001808311612d9457829003601f168201915b5050505050905090565b606060cc8054612d3890615653565b6001600160a01b038216612df457600060405163b752672760e01b81526004016108ab9190614c61565b6001600160a01b03909116600081815261010060209081526040808320805461ffff9096167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009096168617905593825261010190529190912080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055565b60005b83811015611fcb57828282818110612e9557612e95614c32565b9050602002016020810190612eaa91906156a0565b6101066000878785818110612ec157612ec1614c32565b9050602002016020810190612ed691906143e6565b6001600160a01b03168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836003811115612f2757612f27614321565b021790555080612f36816156bb565b915050612e7b565b60007f2688586d6b99be5cf214398e9086bda2b59fc54c339686c166460bc34d3372db612f73612f6e858061510d565b613acb565b612f83612f6e602087018761510d565b612f8f86604001613b30565b612f9b8760a001613b30565b612fad61012089016101008a016143e6565b6101208901356101408a0135612fcb6101808c016101608d016143e6565b60408051602081019a909a528901979097526060880195909552608087019390935260a08601919091526001600160a01b0390811660c086015260e085019190915261010084019190915216610120820152610180808501356101408301526101a08501356101608301520160405160208183030381529060405280519060200120905060006130648361305e84613ba3565b90613beb565b90506001600160a01b038116613082610120860161010087016143e6565b6001600160a01b031614610a945760006040517fb6cd316c0000000000000000000000000000000000000000000000000000000081526004016108ab91906156f3565b600080806130d38480615700565b9050905060005b818110156131ad5760026130ee8680615700565b838181106130fe576130fe614c32565b905060800201602001602081019061311691906156a0565b600381111561312757613127614321565b0361314057613139620138808461544e565b92506131a5565b600161314c8680615700565b8381811061315c5761315c614c32565b905060800201602001602081019061317491906156a0565b600381111561318557613185614321565b036131965761313961ea608461544e565b6131a261ea608461544e565b92505b6001016130da565b50909392505050565b600054610100900460ff166132335760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b61139033612cbf565b600054610100900460ff166132b95760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b609780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b600054610100900460ff16611dd85760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b600054610100900460ff166133dd5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108ab565b81516133f09060cb906020850190614278565b5080516134049060cc906020840190614278565b5050600060c981905560ca5550565b60008061341f866139d3565b81604051602001613431929190615768565b604051602081830303815290604052805190602001209050613452856139d3565b81604051602001613464929190615768565b60408051601f1981840301815282825280516020918201207fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606098891b8116838601529690971b909516603483015260488083019690965280518083039096018652606890910190525050815191012092915050565b60006134e882600161544e565b835110156135385760405162461bcd60e51b815260206004820152601360248201527f746f55696e74385f6f75744f66426f756e64730000000000000000000000000060448201526064016108ab565b50016001015190565b600061354e82602061544e565b8351101561359e5760405162461bcd60e51b815260206004820152601560248201527f746f427974657333325f6f75744f66426f756e6473000000000000000000000060448201526064016108ab565b50016020015190565b6040805160208101909152606081526000806135c38585613c0f565b90506135d060208561544e565b935060008167ffffffffffffffff8111156135ed576135ed614401565b60405190808252806020026020018201604052801561364a57816020015b613637604080516080810190915260008082526020820190815260200160008152602001600081525090565b81526020019060019003908161360b5790505b50905060005b8281101561373f576000613664888861375c565b905061367160148861544e565b9650600061367f89896134db565b905061368c60018961544e565b9750600061369a8a8a613c0f565b90506136a760208a61544e565b985060006136b58b8b613c0f565b90506136c260208b61544e565b99506040518060800160405280856001600160a01b031681526020018460ff1660038111156136f3576136f3614321565b600381111561370457613704614321565b81526020018381526020018281525086868151811061372557613725614c32565b602002602001018190525084600101945050505050613650565b5060408051602081019091529081529250839150505b9250929050565b600061376982601461544e565b835110156137b95760405162461bcd60e51b815260206004820152601560248201527f746f416464726573735f6f75744f66426f756e6473000000000000000000000060448201526064016108ab565b5001602001516c01000000000000000000000000900490565b60006137df82600261544e565b8351101561382f5760405162461bcd60e51b815260206004820152601460248201527f746f55696e7431365f6f75744f66426f756e647300000000000000000000000060448201526064016108ab565b50016002015190565b60606124706040518060a0016040528060018081111561385a5761385a614321565b815260200187815260200186600181111561387757613877614321565b81526020018561ffff1681526020018461ffff16815250613c6c565b60007fd3247e1ccfbc713da9f49ef968b862c31f14fe1e023f90ba13600a71181ac222833560208501356138cd60608701604088016143e6565b60408051602081019590955284019290925260608301526001600160a01b0316608082015260a001604051602081830303815290604052805190602001209050600061391c8361305e84613ba3565b90506001600160a01b03811661393860608601604087016143e6565b6001600160a01b031614610a945760016040517fb6cd316c0000000000000000000000000000000000000000000000000000000081526004016108ab91906156f3565b600061398a83600001516139d3565b8260405160200161399c929190615768565b6040516020818303038152906040528051906020012091506139c183602001516139d3565b8260405160200161299b929190615768565b805151606090819060005b81811015613a9d5782856000015182815181106139fd576139fd614c32565b60200260200101516000015186600001518381518110613a1f57613a1f614c32565b60200260200101516020015187600001518481518110613a4157613a41614c32565b60200260200101516040015188600001518581518110613a6357613a63614c32565b602002602001015160600151604051602001613a8395949392919061578a565b60408051601f1981840301815291905292506001016139de565b50835151604051613ab3919084906020016157f5565b60405160208183030381529060405292505050919050565b6000807f9b3f9d9f3eb04dc5a8b72580a277c2f2872fd789e610f0a829d79ae3e7f714b6613b01613afc8580614c7b565b613c9b565b613b11613afc6020870187614c7b565b604080516020810194909452830191909152606082015260800161299b565b6000807ff3dc3f300b547e0608608d715d2fa24083d708c2d860f33f0cf1f68081a25d60613b6160208501856143e6565b613b7160408601602087016143e6565b6040805160208101949094526001600160a01b039283168482015291166060830152840135608082015260a00161299b565b6000610e8a613bb0613dd1565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6000806000613bfa8585613de0565b91509150613c0781613e22565b509392505050565b6000613c1c82602061544e565b8351101561359e5760405162461bcd60e51b815260206004820152601560248201527f746f55696e743235365f6f75744f66426f756e6473000000000000000000000060448201526064016108ab565b606081600001518260200151836040015184606001518560800151604051602001612ad395949392919061581b565b600080613ca88380615700565b9050905060008167ffffffffffffffff811115613cc757613cc7614401565b604051908082528060200260200182016040528015613cf0578160200160208202803683370190505b50905060005b82811015613d4b57613d26613d0b8680615700565b83818110613d1b57613d1b614c32565b905060800201613f87565b828281518110613d3857613d38614c32565b6020908102919091010152600101613cf6565b5060007fbeeba79171e316d6e21d4aa82d01d380279472a606e309feb5be05fe41d35fa282604051602001613d809190615882565b60405160208183030381529060405280519060200120604051602001613db0929190918252602082015260400190565b60408051601f19818403018152919052805160209091012095945050505050565b6000613ddb613fe6565b905090565b6000808251604103613e165760208301516040840151606085015160001a613e0a8782858561405a565b94509450505050613755565b50600090506002613755565b6000816004811115613e3657613e36614321565b03613e3e5750565b6001816004811115613e5257613e52614321565b03613e9f5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016108ab565b6002816004811115613eb357613eb3614321565b03613f005760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016108ab565b6003816004811115613f1457613f14614321565b03610ea15760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f756500000000000000000000000000000000000000000000000000000000000060648201526084016108ab565b6000807ffc39b93d146fc669d1b5c86a83e369dbd952861827f75099aef588f30ed7d77a613fb860208501856143e6565b613fc860408601602087016156a0565b8560400135866060013560405160200161299b9594939291906158b8565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f61401161411e565b61401961417b565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156140915750600090506003614115565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156140e5573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661410e57600060019250925050614115565b9150600090505b94509492505050565b600080614129612d29565b805190915015614140578051602090910120919050565b60c954801561414f5792915050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4709250505090565b5090565b600080614186612dbb565b80519091501561419d578051602090910120919050565b60ca54801561414f5792915050565b604080516101008101825260008082526020808301919091528251908101835260608152909182019081526020016141f06040518060200160405280606081525090565b8152600060208201819052604082018190526060820181905260809091015290565b60408051610100810182526000815281516020818101909352606081529091820190815260200161424f6040518060200160405280606081525090565b815260006020820181905260408201819052606082018190526080820181905260a09091015290565b82805461428490615653565b90600052602060002090601f0160209004810192826142a657600085556142ec565b82601f106142bf57805160ff19168380011785556142ec565b828001600101855582156142ec579182015b828111156142ec5782518255916020019190600101906142d1565b506141779291505b8082111561417757600081556001016142f4565b60006020828403121561431a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110610ea157610ea1614321565b815181526020820151608082019061437781614350565b8060208401525060ff604084015116604083015260608301511515606083015292915050565b803561ffff811681146143af57600080fd5b919050565b6000602082840312156143c657600080fd5b6114d88261439d565b80356001600160a01b03811681146143af57600080fd5b6000602082840312156143f857600080fd5b6114d8826143cf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516020810167ffffffffffffffff8111828210171561445357614453614401565b60405290565b6040516080810167ffffffffffffffff8111828210171561445357614453614401565b604051610140810167ffffffffffffffff8111828210171561445357614453614401565b604051601f8201601f1916810167ffffffffffffffff811182821017156144c9576144c9614401565b604052919050565b600067ffffffffffffffff8211156144eb576144eb614401565b5060051b60200190565b8035600481106143af57600080fd5b6000602080838503121561451757600080fd5b61451f614430565b9150823567ffffffffffffffff81111561453857600080fd5b8301601f8101851361454957600080fd5b803561455c614557826144d1565b6144a0565b81815260079190911b8201830190838101908783111561457b57600080fd5b928401925b828410156145e057608084890312156145995760008081fd5b6145a1614459565b6145aa856143cf565b81526145b78686016144f5565b818701526040858101359082015260608086013590820152825260809093019290840190614580565b8552509295945050505050565b6000602082840312156145ff57600080fd5b813567ffffffffffffffff81111561461657600080fd5b61136584828501614504565b803560ff811681146143af57600080fd5b60008060006060848603121561464857600080fd5b614651846143cf565b925061465f6020850161439d565b915061466d60408501614622565b90509250925092565b6000806040838503121561468957600080fd5b82359150602083013567ffffffffffffffff8111156146a757600080fd5b8301608081860312156146b957600080fd5b809150509250929050565b600082601f8301126146d557600080fd5b813567ffffffffffffffff8111156146ef576146ef614401565b6147026020601f19601f840116016144a0565b81815284602083860101111561471757600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a0868803121561474c57600080fd5b853567ffffffffffffffff8082111561476457600080fd5b61477089838a016146c4565b965060209150818801358181111561478757600080fd5b8801601f81018a1361479857600080fd5b80356147a6614557826144d1565b81815260059190911b8201840190848101908c8311156147c557600080fd5b8584015b838110156147fd578035868111156147e15760008081fd5b6147ef8f89838901016146c4565b8452509186019186016147c9565b509850505050604088013594506148199150506060870161439d565b949793965091946080013592915050565b60006020828403121561483c57600080fd5b6114d882614622565b60006101c0828403121561485857600080fd5b50919050565b60008060008084860360c081121561487557600080fd5b853567ffffffffffffffff8082111561488d57600080fd5b61489989838a01614845565b965060208801359150808211156148af57600080fd5b6148bb89838a016146c4565b955060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0840112156148ed57600080fd5b60408801945060a088013592508083111561490757600080fd5b5050614915878288016146c4565b91505092959194509250565b60005b8381101561493c578181015183820152602001614924565b83811115610a945750506000910152565b60008151808452614965816020860160208601614921565b601f01601f19169290920160200192915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e0818401526149b560e084018a61494d565b83810360408501526149c7818a61494d565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b81811015614a19578351835292840192918401916001016149fd565b50909c9b505050505050505050505050565b60008060408385031215614a3e57600080fd5b614a47836143cf565b946020939093013593505050565b60008060408385031215614a6857600080fd5b823591506020830135600281106146b957600080fd5b60008060408385031215614a9157600080fd5b614a9a836143cf565b9150614aa86020840161439d565b90509250929050565b60008083601f840112614ac357600080fd5b50813567ffffffffffffffff811115614adb57600080fd5b6020830191508360208260051b850101111561375557600080fd5b60008060008060408587031215614b0c57600080fd5b843567ffffffffffffffff80821115614b2457600080fd5b614b3088838901614ab1565b90965094506020870135915080821115614b4957600080fd5b50614b5687828801614ab1565b95989497509550505050565b60008060408385031215614b7557600080fd5b823567ffffffffffffffff80821115614b8d57600080fd5b614b9986838701614845565b93506020850135915080821115614baf57600080fd5b50614bbc858286016146c4565b9150509250929050565b60006020828403121561485857600080fd5b60008060408385031215614beb57600080fd5b823567ffffffffffffffff80821115614c0357600080fd5b614c0f86838701614bc6565b93506020850135915080821115614c2557600080fd5b50614bbc85828601614bc6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020810160138310614c7557614c75614321565b91905290565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1833603018112614caf57600080fd5b9190910192915050565b6000602080840183357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1853603018112614cf257600080fd5b8401803567ffffffffffffffff811115614d0b57600080fd5b8060071b3603861315614d1d57600080fd5b838752918290526040919083019082870160005b82811015614d89576001600160a01b03614d4a856143cf565b168252614d588685016144f5565b614d6181614350565b8287015283850135858301526060808501359083015260809384019390910190600101614d31565b50979650505050505050565b604081526000614da86040830185614cb9565b90506001600160a01b03831660208301529392505050565b600060208284031215614dd257600080fd5b815180151581146114d857600080fd5b8051602080845281518482018190526000926040919083019082870190855b81811015614e4d5783516001600160a01b03815116845286810151614e2581614350565b8488015280860151868501526060908101519084015292850192608090920191600101614e01565b5090979650505050505050565b6000610100825184526020830151816020860152614e7a82860182614de2565b91505060408301518482036040860152614e948282614de2565b91505060608301516001600160a01b03808216606087015260808501519150614ebc82614350565b8160808701528060a08601511660a0870152505060c0830151614ee460c086018260ff169052565b5060e0830151613c0760e086018215159052565b606081526000614f0b6060830186614e5a565b9050614f1684614350565b83602083015267ffffffffffffffff83166040830152949350505050565b600060408284031215614f4657600080fd5b6040516040810167ffffffffffffffff8282108183111715614f6a57614f6a614401565b816040528293508435915080821115614f8257600080fd5b614f8e86838701614504565b83526020850135915080821115614fa457600080fd5b50614fb185828601614504565b6020830152505092915050565b600060608284031215614fd057600080fd5b6040516060810181811067ffffffffffffffff82111715614ff357614ff3614401565b604052905080615002836143cf565b8152615010602084016143cf565b6020820152604083013560408201525092915050565b60006101c0823603121561503957600080fd5b61504161447c565b823567ffffffffffffffff8082111561505957600080fd5b61506536838701614f34565b8352602085013591508082111561507b57600080fd5b5061508836828601614f34565b60208301525061509b3660408501614fbe565b60408201526150ad3660a08501614fbe565b60608201526101006150c08185016143cf565b60808301526101208085013560a084015261014085013560c08401526150e961016086016143cf565b60e0840152610180850135828401526101a085013581840152505080915050919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112614caf57600080fd5b6000610e8a3683614504565b6001600160a01b038061515f836143cf565b1683528061516f602084016143cf565b16602084015250604090810135910152565b60e08152600061519460e0830186614e5a565b90506151a3602083018561514d565b611365608083018461514d565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126151e457600080fd5b90910192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18336030181126151e457600080fd5b600061522d82836151ed565b6040845261523e6040850182614cb9565b905061524d60208401846151ed565b84820360208601526124708282614cb9565b60006101c061526e83846151b0565b81855261527d82860182615221565b91505061528d60208401846151b0565b848203602086015261529f8282615221565b9150506152b2604085016040850161514d565b6152c260a0850160a0850161514d565b6101006152d08185016143cf565b6001600160a01b031690850152610120838101359085015261014080840135908501526101606153018185016143cf565b6001600160a01b03169085015261018083810135908501526101a09283013592909301919091525090565b60608152600061533f606083018661525f565b82810360208401526153518186614e5a565b91505067ffffffffffffffff83166040830152949350505050565b60208101614c7583614350565b6040808252810184905260008560608301825b878110156153ba576001600160a01b036153a5846143cf565b1682526020928301929091019060010161538c565b5083810360208581019190915285825291508590820160005b868110156153ff576153e4836144f5565b6153ed81614350565b825291830191908301906001016153d3565b5098975050505050505050565b6020815260006114d8602083018461525f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156154615761546161541f565b500190565b60e08152600061547960e0830186614e5a565b90506154ac60208301856001600160a01b0380825116835280602083015116602084015250604081015160408301525050565b82516001600160a01b03908116608084015260208401511660a0830152604083015160c0830152611365565b600061ffff808a1683526001600160a01b03808a16602085015260e0604085015261550660e085018a61494d565b925087606085015286608085015281861660a085015280851660c0850152505098975050505050505050565b60006020828403121561554457600080fd5b815167ffffffffffffffff811681146114d857600080fd5b60028110610ea157610ea1614321565b6155758961555c565b8860f81b815287600182015260008751615596816021850160208c01614921565b8751908301906155ad816021840160208c01614921565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606098891b81169190920160218101919091529590961b9095166035850152507fffff00000000000000000000000000000000000000000000000000000000000060f092831b81166049850152911b16604b820152604d0195945050505050565b6000806040838503121561564257600080fd5b505080516020909101519092909150565b600181811c9082168061566757607f821691505b602082108103614858577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602082840312156156b257600080fd5b6114d8826144f5565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036156ec576156ec61541f565b5060010190565b60208101614c758361555c565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261573557600080fd5b83018035915067ffffffffffffffff82111561575057600080fd5b6020019150600781901b360382131561375557600080fd5b6000835161577a818460208801614921565b9190910191825250602001919050565b6000865161579c818460208b01614921565b80830190507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b1681526157d286614350565b60f89590951b601486015250506015830191909152603582015260550192915050565b8281526000825161580d816020850160208701614921565b919091016020019392505050565b6158248661555c565b8560f81b81528460018201526158398461555c565b60f89390931b60218401527fffff00000000000000000000000000000000000000000000000000000000000060f092831b81166022850152911b16602482015260260192915050565b815160009082906020808601845b838110156158ac57815185529382019390820190600101615890565b50929695505050505050565b8581526001600160a01b038516602082015260a081016158d785614350565b846040830152836060830152826080830152969550505050505056fea2646970667358221220c844c17fae3010e0a0774537478dfd129b230e3b6cd52b5ce75c673ba83435c764736f6c634300080d0033

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
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.