ETH Price: $2,858.57 (-2.81%)

Contract

0x14CA89ac9Cd73B01Bf71a3aF3f8cf8fd224d6A1d

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Transaction Hash
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CrossDelegateV4

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 26 : CrossDelegateV4.sol
// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity 0.8.18;

import "./CrossStorageV4.sol";
import "./lib/RapidityLibV4.sol";
import "./lib/NFTLibV1.sol";

/**
 * @title CrossDelegateV4
 * @dev Main implementation contract for cross-chain functionality
 * This contract handles:
 * - Cross-chain token transfers
 * - NFT transfers
 * - Fee management
 * - Admin and operator role management
 * - Transaction verification and execution
 */
contract CrossDelegateV4 is CrossStorageV4 {
    using SafeMath for uint;

    /**
     * @notice Events emitted by the contract
     */

    /**
     * @notice Emitted when a new admin is set
     * @param adminAccount The address of the new admin
     */
    event SetAdmin(address adminAccount);

    /**
     * @notice Emitted when fees are updated for cross-chain operations
     * @param srcChainID Source chain identifier
     * @param destChainID Destination chain identifier
     * @param contractFee Fee charged by the contract
     * @param agentFee Fee charged by the agent
     */
    event SetFee(uint indexed srcChainID, uint indexed destChainID, uint contractFee, uint agentFee);

    /**
     * @notice Emitted when token pair fees are updated
     * @param tokenPairID ID of the token pair
     * @param contractFee Fee charged by the contract for this token pair
     */
    event SetTokenPairFee(uint indexed tokenPairID, uint contractFee);

    /**
     * @notice Emitted when a storeman group withdraws original coins to a receiver
     * @param smgID ID of the storeman group
     * @param timeStamp Timestamp of the withdrawal
     * @param receiver Address of the receiver
     * @param fee Shadow coin fee received by the storeman group
     */
    event WithdrawHistoryFeeLogger(bytes32 indexed smgID, uint indexed timeStamp, address indexed receiver, uint fee);

    /**
     * @notice Emitted when operator status is configured
     * @param operator Address of the operator
     * @param enabled Whether the operator is enabled or disabled
     */
    event ConfigOperator(address indexed operator, bool indexed enabled);

    /**
     * @notice Emitted when admin status is configured
     * @param admin Address of the admin
     * @param enabled Whether the admin is enabled or disabled
     */
    event ConfigAdmin(address indexed admin, bool indexed enabled);

    /**
     *
     * MODIFIERS
     *
     */

    /**
     * @notice Ensures the caller has admin privileges
     * @dev Checks if the caller is an admin, the main admin, or the owner
     */
    modifier onlyAdmin() {
        require(isAdmin[msg.sender] || msg.sender == admin || msg.sender == owner, "not admin");
        _;
    }

    /**
     * @notice Ensures the caller has operator privileges
     * @dev Checks if the caller is an operator, an admin, the main admin, or the owner
     */
    modifier onlyOperator() {
        require(isOperator[msg.sender] || isAdmin[msg.sender] || msg.sender == admin || msg.sender == owner, "not operator");
        _;
    }

    /**
     * @notice Ensures the storeman group is in ready state
     * @dev Checks if the specified storeman group is ready for operations
     */
    modifier onlyReadySmg(bytes32 smgID) {
        uint8 status;
        uint startTime;
        uint endTime;
        (status,startTime,endTime) = storageData.smgAdminProxy.getStoremanGroupStatus(smgID);

        require(status == uint8(GroupStatus.ready) && block.timestamp >= startTime && block.timestamp <= endTime, "PK is not ready");
        _;
    }


    /**
     *
     * MANIPULATIONS
     *
     */
    /**
     * @notice Initiates a cross-chain token transfer by locking original tokens
     * @dev This function handles the initial step of cross-chain transfer where original tokens are locked
     * @param smgID ID of the storeman group handling the transfer
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param value Amount of tokens to transfer
     * @param userAccount Account information for receiving tokens on the destination chain
     * Requirements:
     * - Contract must not be halted
     * - Storeman group must be ready
     * - Value must be greater than 0
     * - Token pair must exist
     */
    function userLock(bytes32 smgID, uint tokenPairID, uint value, bytes calldata userAccount)
    external
    payable
    notHalted
    nonReentrant
    onlyReadySmg(smgID)
    {
        address smgFeeProxy = getSmgFeeProxy();

        RapidityLibV4.RapidityUserLockParams memory params = RapidityLibV4.RapidityUserLockParams({
        smgID: smgID,
        tokenPairID: tokenPairID,
        value: value,
        currentChainID: currentChainID,
        tokenPairContractFee: mapTokenPairContractFee[tokenPairID],
        etherTransferGasLimit: getEtherTransferGasLimit(),
        destUserAccount: userAccount,
        smgFeeProxy: smgFeeProxy
        });
        RapidityLibV4.userLock(storageData, params);
    }

    /**
     * @notice Initiates a cross-chain token transfer by burning WRC20 tokens
     * @dev This function handles the initial step of cross-chain transfer where WRC20 tokens are burned
     * @param smgID ID of the storeman group handling the transfer
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param value Amount of tokens to transfer
     * @param fee Fee for the transfer operation
     * @param tokenAccount Address of the token contract
     * @param userAccount Account information for receiving tokens on the destination chain
     * Requirements:
     * - Contract must not be halted
     * - Storeman group must be ready
     * - Value must be greater than fee
     * - Token pair must exist
     */
    function userBurn(bytes32 smgID, uint tokenPairID, uint value, uint fee, address tokenAccount, bytes calldata userAccount)
    external
    payable
    notHalted
    nonReentrant
    onlyReadySmg(smgID)
    {
        address smgFeeProxy = getSmgFeeProxy();

        RapidityLibV4.RapidityUserBurnParams memory params = RapidityLibV4.RapidityUserBurnParams({
        smgID: smgID,
        tokenPairID: tokenPairID,
        value: value,
        fee: fee,
        currentChainID: currentChainID,
        tokenPairContractFee: mapTokenPairContractFee[tokenPairID],
        etherTransferGasLimit: getEtherTransferGasLimit(),
        srcTokenAccount: tokenAccount,
        destUserAccount: userAccount,
        smgFeeProxy: smgFeeProxy
        });
        RapidityLibV4.userBurn(storageData, params);
    }

    /**
     * @notice Mints WRC20 tokens for cross-chain transfer
     * @dev This function is called by the storeman group to mint WRC20 tokens after receiving original tokens
     * @param uniqueID Unique identifier for the cross-chain transaction
     * @param smgID ID of the storeman group handling the transfer
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param value Amount of tokens to mint
     * @param fee Fee for the transfer operation
     * @param tokenAccount Address of the token contract
     * @param userAccount Address of the user to receive the minted tokens
     * @param r First part of the signature
     * @param s Second part of the signature
     * Requirements:
     * - Contract must not be halted
     * - Signature must be valid
     * - Transaction must not be already processed
     * - Value must be greater than fee
     */
    function smgMint(bytes32 uniqueID, bytes32 smgID, uint tokenPairID, uint value, uint fee, address tokenAccount, address userAccount, bytes calldata r, bytes32 s)
    external
    notHalted
    {
        uint curveID;
        bytes memory PK;
        (curveID, PK) = acquireReadySmgInfo(smgID);

        RapidityLibV4.RapiditySmgMintParams memory params = RapidityLibV4.RapiditySmgMintParams({
        uniqueID: uniqueID,
        smgID: smgID,
        tokenPairID: tokenPairID,
        value: value,
        fee: fee,
        destTokenAccount: tokenAccount,
        destUserAccount: userAccount,
        smgFeeProxy: (storageData.smgFeeProxy == address(0)) ? owner : storageData.smgFeeProxy // fix: Stack too deep
        });
        RapidityLibV4.smgMint(storageData, params);

        bytes32 mHash = hashFunc(abi.encode(currentChainID, uniqueID, tokenPairID, value, fee, tokenAccount, userAccount));
        verifySignature(curveID, mHash, PK, r, s);
    }

    /**
     * @notice Releases original tokens in exchange for WRC20 tokens on Wanchain
     * @dev This function is called by the storeman group to release original tokens after receiving WRC20 tokens
     * @param uniqueID Unique identifier for the cross-chain transaction
     * @param smgID ID of the storeman group handling the transfer
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param value Amount of tokens to release
     * @param fee Fee for the transfer operation
     * @param tokenAccount Address of the token contract
     * @param userAccount Address of the user to receive the original tokens
     * @param r First part of the signature
     * @param s Second part of the signature
     * Requirements:
     * - Contract must not be halted
     * - Storeman group must be ready and valid
     * - Signature must be valid
     * - Transaction must not be already processed
     */
    function smgRelease(bytes32 uniqueID, bytes32 smgID, uint tokenPairID, uint value, uint fee, address tokenAccount, address userAccount, bytes calldata r, bytes32 s)
    external
    notHalted
    {
        uint curveID;
        bytes memory PK;
        (curveID, PK) = acquireReadySmgInfo(smgID);

        RapidityLibV4.RapiditySmgReleaseParams memory params = RapidityLibV4.RapiditySmgReleaseParams({
        uniqueID: uniqueID,
        smgID: smgID,
        tokenPairID: tokenPairID,
        value: value,
        fee: fee,
        etherTransferGasLimit: getEtherTransferGasLimit(),
        destTokenAccount: tokenAccount,
        destUserAccount: userAccount,
        smgFeeProxy: (storageData.smgFeeProxy == address(0)) ? owner : storageData.smgFeeProxy // fix: Stack too deep
        });
        RapidityLibV4.smgRelease(storageData, params);

        bytes32 mHash = hashFunc(abi.encode(currentChainID, uniqueID, tokenPairID, value, fee, tokenAccount, userAccount));
        verifySignature(curveID, mHash, PK, r, s);
    }

    /**
     * @notice Sets the fees for cross-chain transfers between specific chains
     * @dev This function allows operators to set both contract and agent fees for cross-chain operations
     * @param param Struct containing the fee parameters:
     *        - srcChainID: Source chain ID
     *        - destChainID: Destination chain ID
     *        - contractFee: Fee charged by the contract
     *        - agentFee: Fee charged by the agent
     * Requirements:
     * - Caller must be an operator
     */
    function setFee(SetFeesParam calldata param) public virtual onlyOperator {
        storageData.mapContractFee[param.srcChainID][param.destChainID] = param.contractFee;
        storageData.mapAgentFee[param.srcChainID][param.destChainID] = param.agentFee;
        emit SetFee(param.srcChainID, param.destChainID, param.contractFee, param.agentFee);
    }

    /**
     * @notice Sets fees for multiple cross-chain transfer pairs at once
     * @dev This function allows operators to set fees for multiple chain pairs in a single transaction
     * @param params Array of fee parameters for different chain pairs
     * Requirements:
     * - Caller must be an operator
     */
    function setFees(SetFeesParam [] calldata params) public virtual onlyOperator {
        for (uint i = 0; i < params.length; ++i) {
            storageData.mapContractFee[params[i].srcChainID][params[i].destChainID] = params[i].contractFee;
            storageData.mapAgentFee[params[i].srcChainID][params[i].destChainID] = params[i].agentFee;
            emit SetFee(params[i].srcChainID, params[i].destChainID, params[i].contractFee, params[i].agentFee);
        }
    }

    /**
     * @notice Sets the contract fee for a specific token pair
     * @dev This function allows operators to set the contract fee for cross-chain transfers of a specific token pair
     * @param tokenPairID ID of the token pair
     * @param contractFee Fee charged by the contract for this token pair
     * Requirements:
     * - Caller must be an operator
     */
    function setTokenPairFee(uint256 tokenPairID, uint256 contractFee) external virtual onlyOperator {
        mapTokenPairContractFee[tokenPairID] = contractFee;
        emit SetTokenPairFee(tokenPairID, contractFee);
    }

    /**
     * @notice Sets contract fees for multiple token pairs at once
     * @dev This function allows operators to set contract fees for multiple token pairs in a single transaction
     * @param params Array of token pair fee parameters
     * Requirements:
     * - Caller must be an operator
     */
    function setTokenPairFees(SetTokenPairFeesParam [] calldata params) public virtual onlyOperator {
        for (uint i = 0; i < params.length; ++i) {
            mapTokenPairContractFee[params[i].tokenPairID] = params[i].contractFee;
            emit SetTokenPairFee(params[i].tokenPairID, params[i].contractFee);
        }
    }

    /**
     * @notice Sets the current chain ID for the contract
     * @dev This function allows admin to set the chain ID only if it hasn't been set before
     * @param chainID The chain ID to set
     * Requirements:
     * - Caller must be admin
     * - Chain ID must not be already set
     */
    function setChainID(uint256 chainID) external virtual onlyAdmin {
        if (currentChainID == 0) {
            currentChainID = chainID;
        }
    }

    /**
     * @notice Computes the hash of input data using either keccak256 or sha256
     * @dev This function is used for signature verification in cross-chain transactions
     * @param data The input data to hash
     * @return The computed hash value
     */
    function hashFunc(bytes memory data) public view returns (bytes32){
        if(hashType == 1) {
            return keccak256(data);
        } else {
            return sha256(data);
        }
    }

    /**
     * @notice Sets the hash function type to be used for signature verification
     * @dev This function allows the owner to switch between keccak256 (1) and sha256 (0)
     * @param _hashType The hash function type to set (1 for keccak256, 0 for sha256)
     * Requirements:
     * - Caller must be the owner
     */
    function setHashType(uint _hashType) external onlyOwner {
        hashType = _hashType;
    }

    /**
     * @notice Sets the admin address for the contract
     * @dev This function allows the owner to change the admin address
     * @param adminAccount The new admin address
     * Requirements:
     * - Caller must be the owner
     */
    function setAdmin(address adminAccount) external onlyOwner {
        admin = adminAccount;
        emit SetAdmin(adminAccount);
    }

    /**
     * @notice Sets a uint value in the contract's storage
     * @dev This function allows admin to set values in the contract's storage using key-value pairs
     * @param key The primary key for the storage location
     * @param innerKey The secondary key for the storage location
     * @param value The uint value to store
     * Requirements:
     * - Caller must be admin
     */
    function setUintValue(bytes calldata key, bytes calldata innerKey, uint value) external virtual onlyAdmin {
        return BasicStorageLib.setStorage(uintData, key, innerKey, value);
    }

    /**
     * @notice Deletes a uint value from the contract's storage
     * @dev This function allows admin to remove values from the contract's storage
     * @param key The primary key for the storage location
     * @param innerKey The secondary key for the storage location
     * Requirements:
     * - Caller must be admin
     */
    function delUintValue(bytes calldata key, bytes calldata innerKey) external virtual onlyAdmin {
        return BasicStorageLib.delStorage(uintData, key, innerKey);
    }

    /// @notice                             update the initialized state value of this contract
    /// @param tokenManager                 address of the token manager
    /// @param smgAdminProxy                address of the storeman group admin
    /// @param smgFeeProxy                  address of the proxy to store fee for storeman group
    /// @param sigVerifier                  address of the signature verifier
    function setPartners(address tokenManager, address smgAdminProxy, address smgFeeProxy, address, address sigVerifier)
    external
    onlyOwner
    {
        require(tokenManager != address(0) && smgAdminProxy != address(0) && sigVerifier != address(0),
            "Parameter is invalid");

        storageData.smgAdminProxy = IStoremanGroup(smgAdminProxy);
        storageData.tokenManager = ITokenManager(tokenManager);
        // storageData.quota = IQuota(quota);
        storageData.smgFeeProxy = smgFeeProxy;
        storageData.sigVerifier = ISignatureVerifier(sigVerifier);
    }


    /**
     * @notice Withdraws accumulated historical fees to the foundation account
     * @dev This function allows withdrawing fees accumulated by storeman groups
     * @param smgIDs Array of storeman group IDs whose fees are to be withdrawn
     * Requirements:
     * - smgFeeProxy must be a valid address
     */
    function smgWithdrawHistoryFee(bytes32 [] calldata smgIDs) external {
        uint fee;
        uint currentFee;
        address smgFeeProxy = storageData.smgFeeProxy;
        if (smgFeeProxy == address(0)) {
            smgFeeProxy = owner;
        }
        require(smgFeeProxy != address(0), "invalid smgFeeProxy");

        for (uint i = 0; i < smgIDs.length; ++i) {
            currentFee = storageData.mapStoremanFee[smgIDs[i]];
            delete storageData.mapStoremanFee[smgIDs[i]];
            fee = fee.add(currentFee);
            emit WithdrawHistoryFeeLogger(smgIDs[i], block.timestamp, smgFeeProxy, currentFee);
        }
        if (fee > 0) {
            EtherTransfer.sendValue(payable(smgFeeProxy), fee, getEtherTransferGasLimit());
        }
    }


    /**
     * @notice Retrieves a uint value from the contract's storage
     * @dev This function allows reading values from the contract's storage using key-value pairs
     * @param key The primary key for the storage location
     * @param innerKey The secondary key for the storage location
     * @return The stored uint value
     */
    function getUintValue(bytes calldata key, bytes calldata innerKey) public view returns (uint) {
        return BasicStorageLib.getStorage(uintData, key, innerKey);
    }

    /**
     * @notice Retrieves the accumulated fee for a specific storeman group
     * @dev This function allows checking the fee amount that a storeman group has accumulated
     * @param key The storeman group ID
     * @return fee The accumulated fee amount for the storeman group
     */
    function getStoremanFee(bytes32 key) external view returns(uint fee) {
        fee = storageData.mapStoremanFee[key];
    }

    /**
     * @notice Retrieves the fees for cross-chain transfers between specific chains
     * @dev This function allows checking both contract and agent fees for a specific chain pair
     * @param param Struct containing the chain IDs to check fees for
     * @return fee Struct containing the contract and agent fees
     */
    function getFee(GetFeesParam calldata param) public view returns(GetFeesReturn memory fee) {
        fee.contractFee = storageData.mapContractFee[param.srcChainID][param.destChainID];
        fee.agentFee = storageData.mapAgentFee[param.srcChainID][param.destChainID];
    }

    /**
     * @notice Retrieves fees for multiple cross-chain transfer pairs at once
     * @dev This function allows checking fees for multiple chain pairs in a single call
     * @param params Array of chain pair parameters to check fees for
     * @return fees Array of fee structs containing contract and agent fees for each chain pair
     */
    function getFees(GetFeesParam [] calldata params) public view returns(GetFeesReturn [] memory fees) {
        fees = new GetFeesReturn[](params.length);
        for (uint i = 0; i < params.length; ++i) {
            fees[i].contractFee = storageData.mapContractFee[params[i].srcChainID][params[i].destChainID];
            fees[i].agentFee = storageData.mapAgentFee[params[i].srcChainID][params[i].destChainID];
        }
    }

    /**
     * @notice Retrieves the contract fee for a specific token pair
     * @dev This function allows checking the contract fee for cross-chain transfers of a specific token pair
     * @param tokenPairID ID of the token pair
     * @return contractFee The contract fee for the specified token pair
     */
    function getTokenPairFee(uint256 tokenPairID) external view returns(uint256 contractFee) {
        contractFee = mapTokenPairContractFee[tokenPairID];
    }

    /**
     * @notice Retrieves contract fees for multiple token pairs at once
     * @dev This function allows checking contract fees for multiple token pairs in a single call
     * @param tokenPairIDs Array of token pair IDs
     * @return contractFees Array of contract fees for each token pair
     */
    function getTokenPairFees(uint256[] calldata tokenPairIDs) external view returns(uint256 [] memory contractFees) {
        contractFees = new uint256[](tokenPairIDs.length);
        for (uint i = 0; i < tokenPairIDs.length; ++i) {
            contractFees[i] = mapTokenPairContractFee[tokenPairIDs[i]];
        }
    }

    /**
     * @notice Retrieves the initialized state and partner addresses of the contract
     * @dev This function returns the addresses of all core components and partner contracts
     * @return tokenManager Address of the token manager contract
     * @return smgAdminProxy Address of the storeman group admin proxy
     * @return smgFeeProxy Address of the proxy to store fees for storeman group
     * @return quota Address of the quota contract
     * @return sigVerifier Address of the signature verifier contract
     */
    function getPartners()
    external
    view
    returns(address tokenManager, address smgAdminProxy, address smgFeeProxy, address quota, address sigVerifier)
    {
        tokenManager = address(storageData.tokenManager);
        smgAdminProxy = address(storageData.smgAdminProxy);
        smgFeeProxy = storageData.smgFeeProxy;
        quota = address(storageData.quota);
        sigVerifier = address(storageData.sigVerifier);
    }


    /** Private and Internal Functions */

    /**
     * @notice Retrieves information about a ready storeman group
     * @dev This function returns the curve ID and public key of a storeman group that is ready for operations
     * @param smgID ID of the storeman group to check
     * @return curveID ID of the elliptic curve used by the storeman group
     * @return PK Public key of the storeman group
     * Requirements:
     * - Storeman group must be in ready status
     */
    function acquireReadySmgInfo(bytes32 smgID)
    internal
    view
    returns (uint curveID, bytes memory PK)
    {
        uint8 status;
        uint startTime;
        uint endTime;
        (,status,,,,curveID,,PK,,startTime,endTime) = storageData.smgAdminProxy.getStoremanGroupConfig(smgID);

        require(status == uint8(GroupStatus.ready) && block.timestamp >= startTime && block.timestamp <= endTime, "PK is not ready");

        return (curveID, PK);
    }

    /**
     * @notice Retrieves information about an unregistered storeman group
     * @dev This function returns the curve ID and public key of a storeman group that is not yet registered
     * @param smgID ID of the storeman group to check
     * @return curveID ID of the elliptic curve used by the storeman group
     * @return PK Public key of the storeman group
     * Requirements:
     * - Storeman group must be in unregistered status
     */
    function acquireUnregisteredSmgInfo(bytes32 smgID)
    internal
    view
    returns (uint curveID, bytes memory PK)
    {
        uint8 status;
        (,status,,,,curveID,,PK,,,) = storageData.smgAdminProxy.getStoremanGroupConfig(smgID);

        require(status == uint8(GroupStatus.unregistered), "PK is not unregistered");
    }

    /**
     * @notice Converts a bytes array to bytes32 starting from a specified offset
     * @dev This function is used for extracting bytes32 values from a bytes array
     * @param b The bytes array to convert
     * @param offset The starting offset in the array
     * @return result The converted bytes32 value
     */
    function bytesToBytes32(bytes memory b, uint offset) internal pure returns (bytes32 result) {
        assembly {
            result := mload(add(add(b, offset), 32))
        }
    }

    /**
     * @notice Verifies a signature using the provided parameters
     * @dev This function verifies a signature using the storeman group's public key and the signature components
     * @param curveID ID of the elliptic curve used for verification
     * @param message The message that was signed
     * @param PK The public key of the signer
     * @param r First component of the signature
     * @param s Second component of the signature
     * Requirements:
     * - Signature must be valid according to the signature verifier contract
     */
    function verifySignature(uint curveID, bytes32 message, bytes memory PK, bytes memory r, bytes32 s) internal {
        bytes32 PKx = bytesToBytes32(PK, 0);
        bytes32 PKy = bytesToBytes32(PK, 32);

        bytes32 Rx = bytesToBytes32(r, 0);
        bytes32 Ry = bytesToBytes32(r, 32);

        require(storageData.sigVerifier.verify(curveID, s, PKx, PKy, Rx, Ry, message), "Signature verification failed");
    }

    /**
     * @notice Gets the address of the storeman group fee proxy
     * @dev This function returns the fee proxy address, falling back to the owner if not set
     * @return The address of the fee proxy or owner
     */
    function getSmgFeeProxy() internal view returns (address) {
        address smgFeeProxy = storageData.smgFeeProxy;
        return (smgFeeProxy == address(0)) ? owner : smgFeeProxy;
    }

    //*********************************************************************************************
    //*********************************************************************************************
    // NFT
    /**
     * @notice Implements the ERC721 token receiver interface
     * @dev This function allows the contract to receive ERC721 tokens
     * address - operator The address which called safeTransferFrom function
     * address - from The address which previously owned the token
     * uint256 - tokenId The token identifier
     * bytes - data Additional data with no specified format
     * @return The function selector of onERC721Received
     */
    function onERC721Received(address, address, uint256, bytes memory)
        public
        pure
        returns(bytes4)
    {
        return this.onERC721Received.selector;
    }

    /**
     * @notice Implements the ERC1155 token receiver interface for single token transfers
     * @dev This function allows the contract to receive ERC1155 tokens
     * address - operator The address which called safeTransferFrom function
     * address - from The address which previously owned the token
     * uint256 - id The token identifier
     * uint256 - value The amount of tokens being transferred
     * bytes - data Additional data with no specified format
     * @return The function selector of onERC1155Received
     */
    function onERC1155Received(address, address, uint256, uint256, bytes memory) 
        public 
        pure 
        returns (bytes4)
    {
        return this.onERC1155Received.selector;
    }

    /**
     * @notice Implements the ERC1155 token receiver interface for batch token transfers
     * @dev This function allows the contract to receive multiple ERC1155 tokens in a single transaction
     * address - operator The address which called safeBatchTransferFrom function
     * address - from The address which previously owned the tokens
     * uint256[] - ids Array of token identifiers
     * uint256[] - values Array of token amounts
     * bytes - data Additional data with no specified format
     * @return The function selector of onERC1155BatchReceived
     */
    function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) 
        public
        pure
        returns (bytes4)
    {
        return this.onERC1155BatchReceived.selector;
    }

    /**
     * @notice Initiates a cross-chain NFT transfer by locking original NFTs
     * @dev This function handles the initial step of cross-chain NFT transfer where original NFTs are locked
     * @param smgID ID of the storeman group handling the transfer
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param tokenIDs Array of NFT token IDs to transfer
     * @param tokenValues Array of token values (amounts) for each NFT
     * @param userAccount Account information for receiving NFTs on the destination chain
     * Requirements:
     * - Contract must not be halted
     * - Storeman group must be ready
     * - Number of tokens must be between 1 and maxBatchSize
     * - Length of tokenIDs and tokenValues must match
     */
    function userLockNFT(bytes32 smgID, uint tokenPairID, uint[] memory tokenIDs, uint[] memory tokenValues, bytes memory userAccount)
        public
        payable
        virtual
        notHalted
        nonReentrant
        onlyReadySmg(smgID)
    {
        require(tokenIDs.length > 0 && tokenIDs.length <= getMaxBatchSize(), "Invalid length");
        require(tokenIDs.length == tokenValues.length, "Length mismatch");

        NFTLibV1.RapidityUserLockNFTParams memory params = NFTLibV1.RapidityUserLockNFTParams({
            smgID: smgID,
            tokenPairID: tokenPairID,
            tokenIDs: tokenIDs,
            tokenValues: tokenValues,
            currentChainID: currentChainID,
            tokenPairContractFee: mapTokenPairContractFee[tokenPairID],
            etherTransferGasLimit: getEtherTransferGasLimit(),
            destUserAccount: userAccount,
            smgFeeProxy: getSmgFeeProxy()
        });
        NFTLibV1.userLockNFT(storageData, params);
    }

    /**
     * @notice Initiates a cross-chain NFT transfer by burning WRC721 tokens
     * @dev This function handles the initial step of cross-chain NFT transfer where WRC721 tokens are burned
     * @param smgID ID of the storeman group handling the transfer
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param tokenIDs Array of NFT token IDs to transfer
     * @param tokenValues Array of token values (amounts) for each NFT
     * @param tokenAccount Address of the token contract
     * @param userAccount Account information for receiving NFTs on the destination chain
     * Requirements:
     * - Contract must not be halted
     * - Storeman group must be ready
     * - Number of tokens must be between 1 and maxBatchSize
     * - Length of tokenIDs and tokenValues must match
     */
    function userBurnNFT(bytes32 smgID, uint tokenPairID, uint[] memory tokenIDs, uint[] memory tokenValues, address tokenAccount, bytes memory userAccount)
        public
        payable
        notHalted
        nonReentrant
        onlyReadySmg(smgID)
    {
        require(tokenIDs.length > 0 && tokenIDs.length <= getMaxBatchSize(), "Invalid length");
        require(tokenIDs.length == tokenValues.length, "Length mismatch");

        NFTLibV1.RapidityUserBurnNFTParams memory params = NFTLibV1.RapidityUserBurnNFTParams({
            smgID: smgID,
            tokenPairID: tokenPairID,
            tokenIDs: tokenIDs,
            tokenValues: tokenValues,
            currentChainID: currentChainID,
            tokenPairContractFee: mapTokenPairContractFee[tokenPairID],
            etherTransferGasLimit: getEtherTransferGasLimit(),
            srcTokenAccount: tokenAccount,
            destUserAccount: userAccount,
            smgFeeProxy: getSmgFeeProxy()
        });
        NFTLibV1.userBurnNFT(storageData, params);
    }

    /**
     * @notice Mints WRC721 tokens for cross-chain NFT transfer
     * @dev This function is called by the storeman group to mint WRC721 tokens after receiving original NFTs
     * @param uniqueID Unique identifier for the cross-chain transaction
     * @param smgID ID of the storeman group handling the transfer
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param tokenIDs Array of NFT token IDs to mint
     * @param tokenValues Array of token values (amounts) for each NFT
     * @param extData Additional data for the transfer
     * @param tokenAccount Address of the token contract
     * @param userAccount Address of the user to receive the minted NFTs
     * @param r First part of the signature
     * @param s Second part of the signature
     * Requirements:
     * - Contract must not be halted
     * - Storeman group must be ready and valid
     * - Signature must be valid
     * - Transaction must not be already processed
     */
    function smgMintNFT(bytes32 uniqueID, bytes32 smgID, uint tokenPairID, uint[] memory tokenIDs, uint[] memory tokenValues, bytes memory extData, address tokenAccount, address userAccount, bytes memory r, bytes32 s)
        public
        notHalted
    {
        uint curveID;
        bytes memory PK;
        (curveID, PK) = acquireReadySmgInfo(smgID);

        NFTLibV1.RapiditySmgMintNFTParams memory params = NFTLibV1.RapiditySmgMintNFTParams({
            uniqueID: uniqueID,
            smgID: smgID,
            tokenPairID: tokenPairID,
            tokenIDs: tokenIDs,
            tokenValues: tokenValues,
            extData: extData,
            destTokenAccount: tokenAccount,
            destUserAccount: userAccount
        });

        NFTLibV1.smgMintNFT(storageData, params);
        bytes32 mHash = hashFunc(abi.encode(currentChainID, uniqueID, tokenPairID, tokenIDs, tokenValues, extData, tokenAccount, userAccount));
        verifySignature(curveID, mHash, PK, r, s);
    }

    /**
     * @notice Releases original NFTs in exchange for WRC721 tokens on Wanchain
     * @dev This function is called by the storeman group to release original NFTs after receiving WRC721 tokens
     * @param uniqueID Unique identifier for the cross-chain transaction
     * @param smgID ID of the storeman group handling the transfer
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param tokenIDs Array of NFT token IDs to release
     * @param tokenValues Array of token values (amounts) for each NFT
     * @param tokenAccount Address of the token contract
     * @param userAccount Address of the user to receive the original NFTs
     * @param r First part of the signature
     * @param s Second part of the signature
     * Requirements:
     * - Contract must not be halted
     * - Storeman group must be ready and valid
     * - Signature must be valid
     * - Transaction must not be already processed
     */
    function smgReleaseNFT(bytes32 uniqueID, bytes32 smgID, uint tokenPairID, uint[] memory tokenIDs, uint[] memory tokenValues, address tokenAccount, address userAccount, bytes memory r, bytes32 s)
        public
        notHalted
    {
        uint curveID;
        bytes memory PK;
        (curveID, PK) = acquireReadySmgInfo(smgID);

        NFTLibV1.RapiditySmgReleaseNFTParams memory params = NFTLibV1.RapiditySmgReleaseNFTParams({
            uniqueID: uniqueID,
            smgID: smgID,
            tokenPairID: tokenPairID,
            tokenIDs: tokenIDs,
            tokenValues: tokenValues,
            destTokenAccount: tokenAccount,
            destUserAccount: userAccount
        });
        NFTLibV1.smgReleaseNFT(storageData, params);

        bytes32 mHash = hashFunc(abi.encode(currentChainID, uniqueID, tokenPairID, tokenIDs, tokenValues, tokenAccount, userAccount));
        verifySignature(curveID, mHash, PK, r, s);
    }

    /**
     * @notice Sets the maximum batch size for NFT transfers
     * @dev This function allows admin to set the maximum number of NFTs that can be transferred in a single transaction
     * @param _maxBatchSize The new maximum batch size
     * Requirements:
     * - Caller must be admin
     */
    function setMaxBatchSize(uint _maxBatchSize) 
      external
      virtual
      onlyAdmin
    {
      maxBatchSize = _maxBatchSize;
    }

    /**
     * @notice Gets the maximum batch size for NFT transfers
     * @dev This function returns the maximum number of NFTs that can be transferred in a single transaction
     * @return The maximum batch size (defaults to 20 if not set)
     */
    function getMaxBatchSize() 
      public
      view
      returns (uint)
    {
      if(maxBatchSize == 0) {
        return 20;
      }
      return maxBatchSize;
    }

    /**
     * @notice Gets the batch fee for NFT transfers
     * @dev This function calculates the fee for transferring a batch of NFTs
     * @param tokenPairID ID of the token pair
     * @param batchLength Number of NFTs in the batch
     * @return The calculated batch fee
     */
    function getBatchFee(uint tokenPairID, uint batchLength) 
      external
      view
      returns (uint)
    {
      uint contractFee;
      (, contractFee) = NFTLibV1.getTokenScAddrAndContractFee(storageData, tokenPairID, mapTokenPairContractFee[tokenPairID], currentChainID, batchLength);
      return contractFee;
    }

    /**
     * @notice Sets the gas limit for ether transfers
     * @dev This function allows admin to set the gas limit used for ether transfers
     * @param _etherTransferGasLimit The new gas limit
     * Requirements:
     * - Caller must be admin
     */
    function setEtherTransferGasLimit(uint _etherTransferGasLimit) 
      external
      virtual
      onlyAdmin
    {
      etherTransferGasLimit = _etherTransferGasLimit;
    }

    /**
     * @notice Gets the gas limit for ether transfers
     * @dev This function returns the gas limit used for ether transfers
     * @return The gas limit (defaults to 2300 if not set)
     */
    function getEtherTransferGasLimit() 
      public
      view
      returns (uint)
    {
      if(etherTransferGasLimit == 0) {
        return 2300;
      }
      return etherTransferGasLimit;
    }

    /**
     * @notice Configures operator status for an address
     * @dev This function allows admin to enable or disable operator privileges for an address
     * @param _operator The address to configure
     * @param enabled Whether to enable or disable operator privileges
     * Requirements:
     * - Caller must be admin
     */
    function configOperator(address _operator, bool enabled) external onlyAdmin {
        isOperator[_operator] = enabled;
        emit ConfigOperator(_operator, enabled);
    }

    /**
     * @notice Configures admin status for an address
     * @dev This function allows owner to enable or disable admin privileges for an address
     * @param _admin The address to configure
     * @param enabled Whether to enable or disable admin privileges
     * Requirements:
     * - Caller must be owner
     */
    function configAdmin(address _admin, bool enabled) external onlyOwner {
        isAdmin[_admin] = enabled;
        emit ConfigAdmin(_admin, enabled);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(
        address[] calldata accounts,
        uint256[] calldata ids
    ) external view returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 6 of 26 : BasicStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

import "../lib/BasicStorageLib.sol";

/**
 * @title BasicStorage
 * @dev Base contract for managing different types of storage data
 * This contract provides basic storage functionality for various data types
 * using the BasicStorageLib library
 * 
 * Key features:
 * - Multiple data type storage support
 * - Library-based storage management
 * - Internal storage access
 * 
 * @custom:usage
 * - Used as base contract for storage functionality
 * - Provides structured storage for different data types
 * - Supports inheritance for storage management
 */
contract BasicStorage {
    /************************************************************
     **
     ** VARIABLES
     **
     ************************************************************/

    //// basic variables
    /**
     * @dev Library usage declarations for different data types
     * 
     * @custom:usage
     * - UintData: For unsigned integer storage
     * - BoolData: For boolean storage
     * - AddressData: For address storage
     * - BytesData: For bytes storage
     * - StringData: For string storage
     */
    using BasicStorageLib for BasicStorageLib.UintData;
    using BasicStorageLib for BasicStorageLib.BoolData;
    using BasicStorageLib for BasicStorageLib.AddressData;
    using BasicStorageLib for BasicStorageLib.BytesData;
    using BasicStorageLib for BasicStorageLib.StringData;

    /**
     * @dev Internal storage variables for different data types
     * 
     * @custom:usage
     * - uintData: Stores unsigned integers
     * - boolData: Stores boolean values
     * - addressData: Stores addresses
     * - bytesData: Stores bytes data
     * - stringData: Stores strings
     * 
     * @custom:security
     * - Internal visibility for controlled access
     * - Library-based storage management
     */
    BasicStorageLib.UintData    internal uintData;
    BasicStorageLib.BoolData    internal boolData;
    BasicStorageLib.AddressData internal addressData;
    BasicStorageLib.BytesData   internal bytesData;
    BasicStorageLib.StringData  internal stringData;
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;
import './Owned.sol';

/**
 * @title Halt
 * @dev Contract for emergency stop functionality
 * This contract provides functionality to halt and resume contract operations
 * in emergency situations
 * 
 * Key features:
 * - Emergency stop mechanism
 * - Access control through ownership
 * - Modifiers for halted state checks
 * 
 * @custom:security
 * - Inherits Owned contract for ownership management
 * - Only owner can halt/resume operations
 * - State checks through modifiers
 */
contract Halt is Owned {

    /**
     * @dev Public state variable indicating if contract is halted
     * 
     * @custom:usage
     * - Controls contract operation state
     * - Accessible for external queries
     * - Modified through setHalt function
     */
    bool public halted = false;

    /**
     * @dev Modifier to ensure contract is not halted
     * 
     * @custom:requirements
     * - Contract must not be in halted state
     * 
     * @custom:reverts
     * - If contract is halted
     */
    modifier notHalted() {
        require(!halted, "Smart contract is halted");
        _;
    }

    /**
     * @dev Modifier to ensure contract is halted
     * 
     * @custom:requirements
     * - Contract must be in halted state
     * 
     * @custom:reverts
     * - If contract is not halted
     */
    modifier isHalted() {
        require(halted, "Smart contract is not halted");
        _;
    }

    /**
     * @dev Sets the halted state of the contract
     * 
     * @param halt Boolean indicating desired halted state
     * 
     * @custom:requirements
     * - Caller must be the contract owner
     * 
     * @custom:effects
     * - Updates halted state
     * - Controls contract operation availability
     */
    function setHalt(bool halt)
        public
        onlyOwner
    {
        halted = halt;
    }
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

/**
 * @title Owned
 * @dev Base contract for ownership management
 * This contract provides functionality for managing contract ownership
 * with support for ownership transfer and renunciation
 * 
 * Key features:
 * - Ownership assignment
 * - Ownership transfer
 * - Ownership renunciation
 * - Two-step ownership transfer
 * 
 * @custom:security
 * - Owner-only access control
 * - Safe ownership transfer
 * - Ownership renunciation capability
 */
contract Owned {

    /**
     * @dev Emitted when ownership is transferred
     * 
     * @param previousOwner Address of the previous owner
     * @param newOwner Address of the new owner
     */
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Modifier to restrict function access to owner only
     * 
     * @custom:requirements
     * - Caller must be the contract owner
     * 
     * @custom:reverts
     * - If caller is not the owner
     */
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    /**
     * @dev Public state variable for contract owner
     * 
     * @custom:usage
     * - Stores current owner address
     * - Accessible for external queries
     * - Modified through ownership functions
     */
    address public owner;

    /**
     * @dev Constructor assigns initial owner
     * 
     * @custom:effects
     * - Sets initial owner to contract deployer
     */
    constructor() {
        owner = msg.sender;
    }

    /**
     * @dev Public state variable for pending owner
     * 
     * @custom:usage
     * - Stores address of pending owner
     * - Used in two-step ownership transfer
     */
    address public newOwner;

    /**
     * @dev Transfers ownership to a new address
     * 
     * @param _newOwner Address of the new owner
     * 
     * @custom:requirements
     * - Caller must be the current owner
     * - New owner address must not be zero
     * 
     * @custom:effects
     * - Updates owner address
     * - Emits OwnershipTransferred event
     */
    function transferOwner(address _newOwner) public onlyOwner {
        require(_newOwner != address(0), "New owner is the zero address");
        emit OwnershipTransferred(owner, _newOwner);
        owner = _newOwner;
    }

    /**
     * @dev Initiates two-step ownership transfer
     * 
     * @param _newOwner Address of the new owner
     * 
     * @custom:requirements
     * - Caller must be the current owner
     * 
     * @custom:effects
     * - Sets pending owner address
     */
    function changeOwner(address _newOwner) public onlyOwner {
        newOwner = _newOwner;
    }

    /**
     * @dev Accepts pending ownership transfer
     * 
     * @custom:requirements
     * - Caller must be the pending owner
     * 
     * @custom:effects
     * - Updates owner address to pending owner
     */
    function acceptOwnership() public {
        if (msg.sender == newOwner) {
            owner = newOwner;
        }
    }

    /**
     * @dev Renounces ownership of the contract
     * 
     * @custom:requirements
     * - Caller must be the current owner
     * 
     * @custom:effects
     * - Sets owner to zero address
     * - Makes contract unowned
     */
    function renounceOwnership() public onlyOwner {
        owner = address(0);
    }
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

/**
 * @title Proxy
 * @dev Base contract for proxy pattern implementation
 * This contract provides functionality for delegating calls to implementation contracts
 * and supports contract upgradeability
 * 
 * Key features:
 * - Implementation contract delegation
 * - Contract upgrade support
 * - Fallback handling
 * - Receive function support
 * 
 * @custom:security
 * - Implementation address validation
 * - Safe delegatecall execution
 * - Proper return data handling
 */
contract Proxy {

    /**
     * @dev Emitted when the implementation contract is upgraded
     * 
     * @param implementation Address of the new implementation contract
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Internal storage for implementation contract address
     * 
     * @custom:usage
     * - Stores current implementation address
     * - Used for delegatecall operations
     * - Modified through upgrade operations
     */
    address internal _implementation;

    /**
     * @dev Returns the current implementation contract address
     * 
     * @return Address of the current implementation contract
     */
    function implementation() public view returns (address) {
        return _implementation;
    }

    /**
     * @dev Internal function to handle fallback calls
     * Delegates all calls to the implementation contract
     * 
     * @custom:requirements
     * - Implementation contract must be set
     * 
     * @custom:effects
     * - Executes delegatecall to implementation
     * - Handles return data
     * 
     * @custom:reverts
     * - If implementation contract is not set
     * - If delegatecall fails
     */
    function _fallback() internal {
        address _impl = _implementation;
        require(_impl != address(0), "implementation contract not set");

        assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize())
            let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
            let size := returndatasize()
            returndatacopy(ptr, 0, size)

            switch result
            case 0 { revert(ptr, size) }
            default { return(ptr, size) }
        }
    }

    /**
     * @dev Fallback function to handle unknown function calls
     * Delegates all calls to the implementation contract
     * 
     * @custom:effects
     * - Forwards call to _fallback
     */
    fallback() external payable {
        return _fallback();
    }

    /**
     * @dev Receive function to handle incoming ETH
     * Delegates all calls to the implementation contract
     * 
     * @custom:effects
     * - Forwards call to _fallback
     */
    receive() external payable {
        return _fallback();
    }
}

File 10 of 26 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

/**
 * @title ReentrancyGuard
 * @dev Abstract contract module that helps prevent reentrant calls to a function
 *
 * Key features:
 * - Prevents reentrant function calls
 * - Gas optimization for refunds
 * - Support for nested function protection
 *
 * @custom:security
 * - Prevents reentrancy attacks
 * - Optimizes gas refunds
 * - Supports private function calls
 * 
 * @custom:usage
 * - Inherit this contract to use nonReentrant modifier
 * - Apply nonReentrant modifier to functions that need protection
 * - Use private functions for nested calls
 */
abstract contract ReentrancyGuard {
    /**
     * @dev Private state variable to track reentrancy status
     * 
     * @custom:usage
     * - true: Function can be entered
     * - false: Function is currently executing
     * 
     * @custom:security
     * - Prevents reentrant calls
     * - Optimizes gas refunds
     */
    bool private _notEntered;

    /**
     * @dev Constructor initializes the reentrancy guard
     * 
     * @custom:effects
     * - Sets initial state to true
     * - Optimizes gas refunds
     * 
     * @custom:security
     * - Ensures proper initialization
     * - Prevents initial reentrancy
     */
    constructor () {
        // Storing an initial 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 percetange 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.
        _notEntered = true;
    }

    /**
     * @dev Modifier to prevent reentrant calls to a function
     * 
     * @custom:requirements
     * - Function must not be currently executing
     * 
     * @custom:effects
     * - Sets _notEntered to false during execution
     * - Restores _notEntered to true after execution
     * 
     * @custom:reverts
     * - If function is already executing
     * 
     * @custom:usage
     * - Apply to functions that need reentrancy protection
     * - Use with private functions for nested calls
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_notEntered, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _notEntered = false;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _notEntered = true;
    }
}

File 11 of 26 : CrossStorage.sol
// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

import "../components/BasicStorage.sol";
import "./lib/CrossTypes.sol";
import "./lib/HTLCTxLib.sol";
import "./lib/RapidityTxLib.sol";

/**
 * @title CrossStorage
 * @dev Storage contract for cross-chain functionality that manages cross-chain related data
 * This contract inherits from BasicStorage and provides storage for:
 * - HTLC (Hash Time-Locked Contract) transactions
 * - Rapidity transactions
 * - Cross-chain types and data
 */
contract CrossStorage is BasicStorage {
    using HTLCTxLib for HTLCTxLib.Data;
    using RapidityTxLib for RapidityTxLib.Data;

    /************************************************************
     **
     ** VARIABLES
     **
     ************************************************************/

    /**
     * @dev Internal storage for cross-chain related data
     */
    CrossTypes.Data internal storageData;

    /**
     * @notice Time period for which assets are locked in HTLC transactions
     * @dev Default value is 36 hours (3600*36 seconds)
     */
    uint public lockedTime = uint(3600*36);

    /**
     * @notice Timeout period for storeman group fee receiver address changes
     * @dev Since storeman group admin receiver address may be changed, system ensures:
     * - New address becomes valid after this timeout
     * - Old address becomes invalid after this timeout
     * Default value is 10 minutes (10*60 seconds)
     */
    uint public smgFeeReceiverTimeout = uint(10*60);

    /**
     * @notice Enumeration of possible states for a storeman group
     * @dev States:
     * - none: Initial state
     * - initial: Group has been initialized
     * - curveSeted: Curve parameters have been set
     * - failed: Group setup has failed
     * - selected: Group has been selected
     * - ready: Group is ready for operations
     * - unregistered: Group has been unregistered
     * - dismissed: Group has been dismissed
     */
    enum GroupStatus { none, initial, curveSeted, failed, selected, ready, unregistered, dismissed }

}

File 12 of 26 : CrossStorageV2.sol
// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

import "../components/Proxy.sol";
import "../components/Halt.sol";
import "../components/ReentrancyGuard.sol";
import "./CrossStorage.sol";

/**
 * @title CrossStorageV2
 * @dev Enhanced version of CrossStorage that adds chain ID and fee management functionality
 * This contract inherits from:
 * - CrossStorage: Base storage functionality
 * - ReentrancyGuard: To prevent reentrancy attacks
 * - Halt: To provide emergency stop functionality
 * - Proxy: To enable implementation upgrades
 */
contract CrossStorageV2 is CrossStorage, ReentrancyGuard, Halt, Proxy {

    /************************************************************
     **
     ** VARIABLES
     **
     ************************************************************/

    /** STATE VARIABLES **/
    /**
     * @notice The chain ID of the current network
     * @dev Used to identify the source chain in cross-chain operations
     */
    uint256 public currentChainID;

    /**
     * @notice The address of the contract administrator
     * @dev Has special privileges for managing the contract
     */
    address public admin;

    /** STRUCTURES **/
    /**
     * @notice Parameters for setting cross-chain fees
     * @dev Used when configuring fees for specific chain pairs
     * @param srcChainID Source chain identifier
     * @param destChainID Destination chain identifier
     * @param contractFee Fee charged by the contract
     * @param agentFee Fee charged by the agent
     */
    struct SetFeesParam {
        uint256 srcChainID;
        uint256 destChainID;
        uint256 contractFee;
        uint256 agentFee;
    }

    /**
     * @notice Parameters for retrieving cross-chain fees
     * @dev Used when querying fees for specific chain pairs
     * @param srcChainID Source chain identifier
     * @param destChainID Destination chain identifier
     */
    struct GetFeesParam {
        uint256 srcChainID;
        uint256 destChainID;
    }

    /**
     * @notice Return structure for fee queries
     * @dev Contains the fee information for a specific chain pair
     * @param contractFee Fee charged by the contract
     * @param agentFee Fee charged by the agent
     */
    struct GetFeesReturn {
        uint256 contractFee;
        uint256 agentFee;
    }
}

File 13 of 26 : CrossStorageV3.sol
// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

import "./CrossStorageV2.sol";

/**
 * @title CrossStorageV3
 * @dev Enhanced version of CrossStorageV2 that adds token pair fee management functionality
 * This contract inherits from CrossStorageV2 and provides:
 * - Mapping for token pair contract fees
 * - Structure for setting token pair fees
 */
contract CrossStorageV3 is CrossStorageV2 {

    /************************************************************
     **
     ** VARIABLES
     **
     ************************************************************/

    /** STATE VARIABLES **/
    /**
     * @notice Mapping from token pair ID to contract fee
     * @dev Used to store and retrieve fees for specific token pairs
     * uint256 - tokenPairID Unique identifier for a token pair
     * uint256 - contractFee The fee charged by the contract for this token pair
     */
    mapping(uint256 => uint256) mapTokenPairContractFee;

    /**
     * @notice Parameters for setting token pair fees
     * @dev Used when configuring fees for specific token pairs
     * @param tokenPairID Unique identifier for a token pair
     * @param contractFee The fee to be charged by the contract for this token pair
     */
    struct SetTokenPairFeesParam {
        uint256 tokenPairID;
        uint256 contractFee;
    }

}

File 14 of 26 : CrossStorageV4.sol
// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

import "./CrossStorageV3.sol";

/**
 * @title CrossStorageV4
 * @dev Enhanced version of CrossStorageV3 that adds batch processing, gas limits, and role-based access control
 * This contract inherits from CrossStorageV3 and provides:
 * - Maximum batch size configuration
 * - Gas limit settings for ether transfers
 * - Hash function type selection
 * - Role-based access control for admin and operator roles
 */
contract CrossStorageV4 is CrossStorageV3 {

    /************************************************************
     **
     ** VARIABLES
     **
     ************************************************************/
    /**
     * @notice Maximum number of transactions that can be processed in a single batch
     * @dev Used to limit the size of batch operations for gas optimization
     */
    uint internal maxBatchSize;

    /**
     * @notice Gas limit for ether transfer operations
     * @dev Used to estimate gas costs for cross-chain ether transfers
     */
    uint internal etherTransferGasLimit;

    /**
     * @notice Type of hash function to be used
     * @dev 0: sha256, 1: keccak256
     * Used for generating transaction hashes in cross-chain operations
     */
    uint public hashType; // 0: sha256, 1: keccak256

    /**
     * @notice Mapping of addresses to admin role status
     * @dev Used for role-based access control of administrative functions
     */
    mapping(address => bool) public isAdmin;

    /**
     * @notice Mapping of addresses to operator role status
     * @dev Used for role-based access control of operational functions
     */
    mapping(address => bool) public isOperator;
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

import "../../interfaces/IRC20Protocol.sol";
import "../../interfaces/IQuota.sol";
import "../../interfaces/IStoremanGroup.sol";
import "../../interfaces/ITokenManager.sol";
import "../../interfaces/ISignatureVerifier.sol";
import "./HTLCTxLib.sol";
import "./RapidityTxLib.sol";

/**
 * @title CrossTypes
 * @dev Library containing common types and utilities for cross-chain operations
 * This library provides:
 * - Data structures for cross-chain transactions
 * - Utility functions for address and token operations
 */
library CrossTypes {
    using SafeMath for uint;

    /**
     * @notice Main data structure for cross-chain operations
     * @dev Contains all necessary data and mappings for cross-chain functionality
     */
    struct Data {
        /**
         * @notice HTLC transaction data storage
         * @dev Stores information about Hash Time-Locked Contract transactions
         */
        HTLCTxLib.Data htlcTxData;

        /**
         * @notice Rapidity transaction data storage
         * @dev Stores information about rapid cross-chain transactions
         */
        RapidityTxLib.Data rapidityTxData;

        /**
         * @notice Quota management interface for storeman group
         * @dev Handles quota allocation and management for storeman groups
         */
        IQuota quota;

        /**
         * @notice Token management interface
         * @dev Handles token pair management and cross-chain token operations
         */
        ITokenManager tokenManager;

        /**
         * @notice Storeman group admin interface
         * @dev Manages storeman group administration and configuration
         */
        IStoremanGroup smgAdminProxy;

        /**
         * @notice Storeman group fee admin address
         * @dev Address responsible for managing storeman group fees
         */
        address smgFeeProxy;

        /**
         * @notice Signature verification interface
         * @dev Handles signature verification for cross-chain transactions
         */
        ISignatureVerifier sigVerifier;

        /**
         * @notice Mapping of storeman group fees
         * @dev Maps storeman group IDs to their respective fees
         */
        mapping(bytes32 => uint) mapStoremanFee;

        /**
         * @notice Mapping of contract fees between chains
         * @dev Maps source chain ID and destination chain ID to contract fees
         */
        mapping(uint => mapping(uint =>uint)) mapContractFee;

        /**
         * @notice Mapping of agent fees between chains
         * @dev Maps source chain ID and destination chain ID to agent fees
         */
        mapping(uint => mapping(uint =>uint)) mapAgentFee;
    }

    /**
     * @notice Converts bytes to address
     * @dev Uses assembly to efficiently convert bytes to address
     * @param b Bytes to convert
     * @return addr The converted address
     */
    function bytesToAddress(bytes memory b) internal pure returns (address addr) {
        assembly {
            addr := mload(add(b,20))
        }
    }

    /**
     * @notice Transfers tokens from the contract to a specified address
     * @dev Verifies the transfer was successful by checking balance changes
     * @param tokenScAddr Address of the token contract
     * @param to Address to receive the tokens
     * @param value Amount of tokens to transfer
     * @return bool True if transfer was successful
     * Requirements:
     * - Transfer must succeed
     * - Balance change must match the transfer amount
     */
    function transfer(address tokenScAddr, address to, uint value)
        internal
        returns(bool)
    {
        uint beforeBalance;
        uint afterBalance;
        IRC20Protocol token = IRC20Protocol(tokenScAddr);
        beforeBalance = token.balanceOf(to);
        (bool success,) = tokenScAddr.call(abi.encodeWithSelector(token.transfer.selector, to, value));
        require(success, "transfer failed");
        afterBalance = token.balanceOf(to);
        return afterBalance == beforeBalance.add(value);
    }

    /**
     * @notice Transfers tokens from one address to another
     * @dev Verifies the transfer was successful by checking balance changes
     * @param tokenScAddr Address of the token contract
     * @param from Address to transfer tokens from
     * @param to Address to receive the tokens
     * @param value Amount of tokens to transfer
     * @return bool True if transfer was successful
     * Requirements:
     * - Transfer must succeed
     * - Balance change must match the transfer amount
     */
    function transferFrom(address tokenScAddr, address from, address to, uint value)
        internal
        returns(bool)
    {
        uint beforeBalance;
        uint afterBalance;
        IRC20Protocol token = IRC20Protocol(tokenScAddr);
        beforeBalance = token.balanceOf(to);
        (bool success,) = tokenScAddr.call(abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
        require(success, "transferFrom failed");
        afterBalance = token.balanceOf(to);
        return afterBalance == beforeBalance.add(value);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

/**
 * @title EtherTransfer
 * @dev Library for safe ether transfer operations
 * This library provides a safer alternative to Solidity's native transfer function
 * by allowing custom gas limits and better error handling
 */
library EtherTransfer {
    /**
     * @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/2023/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, uint256 gasLimit) internal {
        require(address(this).balance >= amount, "EtherTransfer: insufficient balance");

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

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";

/**
 * @title HTLCTxLib
 * @dev Library for managing Hash Time-Locked Contract (HTLC) transactions
 * This library provides functionality for:
 * - User and storeman transaction management
 * - Debt management between storeman groups
 * - Transaction status tracking and verification
 */
library HTLCTxLib {
    using SafeMath for uint;

    /**
     * @notice Enumeration of possible transaction statuses
     * @dev Status flow:
     * - None: Initial state
     * - Locked: Transaction is locked and pending
     * - Redeemed: Transaction has been completed
     * - Revoked: Transaction has been cancelled
     * - AssetLocked: Asset is locked in debt management
     * - DebtLocked: Debt is locked in debt management
     */
    enum TxStatus {None, Locked, Redeemed, Revoked, AssetLocked, DebtLocked}

    /**
     * @notice Parameters for user-initiated HTLC transactions
     * @dev Used when creating new user transactions
     * @param xHash Hash of the HTLC random number
     * @param smgID ID of the selected storeman group
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param value Amount of tokens to transfer
     * @param lockFee Fee for the lock operation
     * @param lockedTime Duration for which the transaction is locked
     */
    struct HTLCUserParams {
        bytes32 xHash;
        bytes32 smgID;
        uint tokenPairID;
        uint value;
        uint lockFee;
        uint lockedTime;
    }

    /**
     * @notice Base structure for all HTLC transactions
     * @dev Contains common fields for all transaction types
     * @param smgID ID of the storeman group
     * @param lockedTime Duration for which the transaction is locked
     * @param beginLockedTime Timestamp when the transaction was locked
     * @param status Current status of the transaction
     */
    struct BaseTx {
        bytes32 smgID;
        uint lockedTime;
        uint beginLockedTime;
        TxStatus status;
    }

    /**
     * @notice Structure for user-initiated transactions
     * @dev Extends BaseTx with user-specific information
     * @param baseTx Base transaction information
     * @param tokenPairID ID of the token pair
     * @param value Amount of tokens
     * @param fee Transaction fee
     * @param userAccount Address of the user initiating the transaction
     */
    struct UserTx {
        BaseTx baseTx;
        uint tokenPairID;
        uint value;
        uint fee;
        address userAccount;
    }

    /**
     * @notice Structure for storeman-initiated transactions
     * @dev Extends BaseTx with storeman-specific information
     * @param baseTx Base transaction information
     * @param tokenPairID ID of the token pair
     * @param value Amount of tokens
     * @param userAccount Address of the user to receive tokens
     */
    struct SmgTx {
        BaseTx baseTx;
        uint tokenPairID;
        uint value;
        address userAccount;
    }

    /**
     * @notice Structure for storeman debt transactions
     * @dev Extends BaseTx with debt-specific information
     * @param baseTx Base transaction information
     * @param srcSmgID ID of the source storeman group
     */
    struct DebtTx {
        BaseTx baseTx;
        bytes32 srcSmgID;
    }

    /**
     * @notice Main data structure for HTLC transactions
     * @dev Contains mappings for all transaction types
     * @param mapHashXUserTxs Mapping of transaction hashes to user transactions
     * @param mapHashXSmgTxs Mapping of transaction hashes to storeman transactions
     * @param mapHashXDebtTxs Mapping of transaction hashes to debt transactions
     */
    struct Data {
        mapping(bytes32 => UserTx) mapHashXUserTxs;
        mapping(bytes32 => SmgTx) mapHashXSmgTxs;
        mapping(bytes32 => DebtTx) mapHashXDebtTxs;
    }

    /**
     * @notice Adds a new user transaction
     * @dev Creates a new user transaction with the provided parameters
     * @param self The storage data structure
     * @param params Parameters for the new transaction
     * Requirements:
     * - Transaction must not already exist
     */
    function addUserTx(Data storage self, HTLCUserParams memory params)
        public
    {
        UserTx memory userTx = self.mapHashXUserTxs[params.xHash];
        // UserTx storage userTx = self.mapHashXUserTxs[params.xHash];
        // require(params.value != 0, "Value is invalid");
        require(userTx.baseTx.status == TxStatus.None, "User tx exists");

        userTx.baseTx.smgID = params.smgID;
        userTx.baseTx.lockedTime = params.lockedTime;
        userTx.baseTx.beginLockedTime = block.timestamp;
        userTx.baseTx.status = TxStatus.Locked;
        userTx.tokenPairID = params.tokenPairID;
        userTx.value = params.value;
        userTx.fee = params.lockFee;
        userTx.userAccount = msg.sender;

        self.mapHashXUserTxs[params.xHash] = userTx;
    }

    /**
     * @notice Redeems a user transaction
     * @dev Used for storeman redeem (outbound) operations
     * @param self The storage data structure
     * @param x The HTLC random number
     * @return xHash The hash of the random number
     * Requirements:
     * - Transaction must be in Locked status
     * - Transaction must not be expired
     */
    function redeemUserTx(Data storage self, bytes32 x)
        external
        returns(bytes32 xHash)
    {
        xHash = sha256(abi.encodePacked(x));

        UserTx storage userTx = self.mapHashXUserTxs[xHash];
        require(userTx.baseTx.status == TxStatus.Locked, "Status is not locked");
        require(block.timestamp < userTx.baseTx.beginLockedTime.add(userTx.baseTx.lockedTime), "Redeem timeout");

        userTx.baseTx.status = TxStatus.Redeemed;

        return xHash;
    }

    /**
     * @notice Revokes a user transaction
     * @dev Allows cancellation of expired transactions
     * @param self The storage data structure
     * @param xHash Hash of the HTLC random number
     * Requirements:
     * - Transaction must be in Locked status
     * - Transaction must be expired
     */
    function revokeUserTx(Data storage self, bytes32 xHash)
        external
    {
        UserTx storage userTx = self.mapHashXUserTxs[xHash];
        require(userTx.baseTx.status == TxStatus.Locked, "Status is not locked");
        require(block.timestamp >= userTx.baseTx.beginLockedTime.add(userTx.baseTx.lockedTime), "Revoke is not permitted");

        userTx.baseTx.status = TxStatus.Revoked;
    }

    /**
     * @notice Retrieves user transaction information
     * @dev Returns all relevant information about a user transaction
     * @param self The storage data structure
     * @param xHash Hash of the HTLC random number
     * @return smgID ID of the storeman group
     * @return tokenPairID ID of the token pair
     * @return value Amount of tokens
     * @return fee Transaction fee
     * @return userAccount Address of the user
     */
    function getUserTx(Data storage self, bytes32 xHash)
        external
        view
        returns (bytes32, uint, uint, uint, address)
    {
        UserTx storage userTx = self.mapHashXUserTxs[xHash];
        return (userTx.baseTx.smgID, userTx.tokenPairID, userTx.value, userTx.fee, userTx.userAccount);
    }

    /**
     * @notice Adds a new storeman transaction
     * @dev Creates a new storeman transaction with the provided parameters
     * @param self The storage data structure
     * @param xHash Hash of the HTLC random number
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param value Amount of tokens
     * @param userAccount Address of the user to receive tokens
     * @param lockedTime Duration for which the transaction is locked
     * Requirements:
     * - Value must be non-zero
     * - Transaction must not already exist
     */
    function addSmgTx(Data storage self, bytes32 xHash, bytes32 smgID, uint tokenPairID, uint value, address userAccount, uint lockedTime)
        external
    {
        SmgTx memory smgTx = self.mapHashXSmgTxs[xHash];
        // SmgTx storage smgTx = self.mapHashXSmgTxs[xHash];
        require(value != 0, "Value is invalid");
        require(smgTx.baseTx.status == TxStatus.None, "Smg tx exists");

        smgTx.baseTx.smgID = smgID;
        smgTx.baseTx.status = TxStatus.Locked;
        smgTx.baseTx.lockedTime = lockedTime;
        smgTx.baseTx.beginLockedTime = block.timestamp;
        smgTx.tokenPairID = tokenPairID;
        smgTx.value = value;
        smgTx.userAccount = userAccount;

        self.mapHashXSmgTxs[xHash] = smgTx;
    }

    /**
     * @notice Redeems a storeman transaction
     * @dev Used for user redeem (inbound) operations
     * @param self The storage data structure
     * @param x The HTLC random number
     * @return xHash The hash of the random number
     * Requirements:
     * - Transaction must be in Locked status
     * - Transaction must not be expired
     */
    function redeemSmgTx(Data storage self, bytes32 x)
        external
        returns(bytes32 xHash)
    {
        xHash = sha256(abi.encodePacked(x));

        SmgTx storage smgTx = self.mapHashXSmgTxs[xHash];
        require(smgTx.baseTx.status == TxStatus.Locked, "Status is not locked");
        require(block.timestamp < smgTx.baseTx.beginLockedTime.add(smgTx.baseTx.lockedTime), "Redeem timeout");

        smgTx.baseTx.status = TxStatus.Redeemed;

        return xHash;
    }

    /**
     * @notice Revokes a storeman transaction
     * @dev Allows cancellation of expired transactions
     * @param self The storage data structure
     * @param xHash Hash of the HTLC random number
     * Requirements:
     * - Transaction must be in Locked status
     * - Transaction must be expired
     */
    function revokeSmgTx(Data storage self, bytes32 xHash)
        external
    {
        SmgTx storage smgTx = self.mapHashXSmgTxs[xHash];
        require(smgTx.baseTx.status == TxStatus.Locked, "Status is not locked");
        require(block.timestamp >= smgTx.baseTx.beginLockedTime.add(smgTx.baseTx.lockedTime), "Revoke is not permitted");

        smgTx.baseTx.status = TxStatus.Revoked;
    }

    /**
     * @notice Retrieves storeman transaction information
     * @dev Returns all relevant information about a storeman transaction
     * @param self The storage data structure
     * @param xHash Hash of the HTLC random number
     * @return smgID ID of the storeman group
     * @return tokenPairID ID of the token pair
     * @return value Amount of tokens
     * @return userAccount Address of the user to receive tokens
     */
    function getSmgTx(Data storage self, bytes32 xHash)
        external
        view
        returns (bytes32, uint, uint, address)
    {
        SmgTx storage smgTx = self.mapHashXSmgTxs[xHash];
        return (smgTx.baseTx.smgID, smgTx.tokenPairID, smgTx.value, smgTx.userAccount);
    }

    /**
     * @notice Adds a new debt transaction
     * @dev Creates a new debt transaction between storeman groups
     * @param self The storage data structure
     * @param xHash Hash of the HTLC random number
     * @param srcSmgID ID of the source storeman group
     * @param destSmgID ID of the destination storeman group
     * @param lockedTime Duration for which the transaction is locked
     * @param status Initial status of the transaction
     * Requirements:
     * - Transaction must not already exist
     * - Status must be either Locked or DebtLocked
     */
    function addDebtTx(Data storage self, bytes32 xHash, bytes32 srcSmgID, bytes32 destSmgID, uint lockedTime, TxStatus status)
        external
    {
        DebtTx memory debtTx = self.mapHashXDebtTxs[xHash];
        // DebtTx storage debtTx = self.mapHashXDebtTxs[xHash];
        require(debtTx.baseTx.status == TxStatus.None, "Debt tx exists");

        debtTx.baseTx.smgID = destSmgID;
        debtTx.baseTx.status = status;//TxStatus.Locked;
        debtTx.baseTx.lockedTime = lockedTime;
        debtTx.baseTx.beginLockedTime = block.timestamp;
        debtTx.srcSmgID = srcSmgID;

        self.mapHashXDebtTxs[xHash] = debtTx;
    }

    /**
     * @notice Redeems a debt transaction
     * @dev Used to complete debt transfer between storeman groups
     * @param self The storage data structure
     * @param x The HTLC random number
     * @return xHash The hash of the random number
     * Requirements:
     * - Transaction must be in Locked or DebtLocked status
     * - Transaction must not be expired
     */
    function redeemDebtTx(Data storage self, bytes32 x, TxStatus status)
        external
        returns(bytes32 xHash)
    {
        xHash = sha256(abi.encodePacked(x));

        DebtTx storage debtTx = self.mapHashXDebtTxs[xHash];
        // require(debtTx.baseTx.status == TxStatus.Locked, "Status is not locked");
        require(debtTx.baseTx.status == status, "Status is not locked");
        require(block.timestamp < debtTx.baseTx.beginLockedTime.add(debtTx.baseTx.lockedTime), "Redeem timeout");

        debtTx.baseTx.status = TxStatus.Redeemed;

        return xHash;
    }

    /**
     * @notice Revokes a debt transaction
     * @dev Allows cancellation of expired debt transactions
     * @param self The storage data structure
     * @param xHash Hash of the HTLC random number
     * Requirements:
     * - Transaction must be in Locked or DebtLocked status
     * - Transaction must be expired
     */
    function revokeDebtTx(Data storage self, bytes32 xHash, TxStatus status)
        external
    {
        DebtTx storage debtTx = self.mapHashXDebtTxs[xHash];
        // require(debtTx.baseTx.status == TxStatus.Locked, "Status is not locked");
        require(debtTx.baseTx.status == status, "Status is not locked");
        require(block.timestamp >= debtTx.baseTx.beginLockedTime.add(debtTx.baseTx.lockedTime), "Revoke is not permitted");

        debtTx.baseTx.status = TxStatus.Revoked;
    }

    /**
     * @notice Retrieves debt transaction information
     * @dev Returns all relevant information about a debt transaction
     * @param self The storage data structure
     * @param xHash Hash of the HTLC random number
     * @return smgID ID of the storeman group
     * @return srcSmgID ID of the source storeman group
     */
    function getDebtTx(Data storage self, bytes32 xHash)
        external
        view
        returns (bytes32, bytes32)
    {
        DebtTx storage debtTx = self.mapHashXDebtTxs[xHash];
        return (debtTx.srcSmgID, debtTx.baseTx.smgID);
    }

    function getLeftTime(uint endTime) private view returns (uint) {
        if (block.timestamp < endTime) {
            return endTime.sub(block.timestamp);
        }
        return 0;
    }

    /// @notice                     function for get debt info
    /// @param xHash                hash of HTLC random number
    /// @return leftTime            the left lock time
    function getLeftLockedTime(Data storage self, bytes32 xHash)
        external
        view
        returns (uint leftTime)
    {
        UserTx storage userTx = self.mapHashXUserTxs[xHash];
        if (userTx.baseTx.status != TxStatus.None) {
            return getLeftTime(userTx.baseTx.beginLockedTime.add(userTx.baseTx.lockedTime));
        }
        SmgTx storage smgTx = self.mapHashXSmgTxs[xHash];
        if (smgTx.baseTx.status != TxStatus.None) {
            return getLeftTime(smgTx.baseTx.beginLockedTime.add(smgTx.baseTx.lockedTime));
        }
        DebtTx storage debtTx = self.mapHashXDebtTxs[xHash];
        if (debtTx.baseTx.status != TxStatus.None) {
            return getLeftTime(debtTx.baseTx.beginLockedTime.add(debtTx.baseTx.lockedTime));
        }
        require(false, 'invalid xHash');
    }
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./RapidityTxLib.sol";
import "./CrossTypes.sol";
import "../../interfaces/ITokenManager.sol";
import "./EtherTransfer.sol";


/**
 * @title NFTLibV1
 * @dev Library for managing NFT cross-chain operations
 * This library provides functionality for:
 * - NFT locking and unlocking
 * - NFT minting and burning
 * - Cross-chain NFT transfer management
 */
library NFTLibV1 {
    using SafeMath for uint;
    using RapidityTxLib for RapidityTxLib.Data;

    /**
     * @notice Enumeration of supported token types for cross-chain operations
     * @dev Defines the types of tokens that can be transferred across chains
     */
    enum TokenCrossType {ERC20, ERC721, ERC1155}


    /**
     * @notice Parameters for user-initiated NFT locking operations
     * @dev Used when users want to lock their NFTs for cross-chain transfer
     * @param smgID ID of the selected storeman group
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param tokenIDs Array of NFT token IDs to be locked
     * @param tokenValues Array of NFT token values (for ERC1155)
     * @param currentChainID ID of the current blockchain
     * @param tokenPairContractFee Fee for the token pair contract
     * @param etherTransferGasLimit Gas limit for ether transfers
     * @param destUserAccount Destination user account address
     * @param smgFeeProxy Address of the proxy for storing storeman group fees
     */
    struct RapidityUserLockNFTParams {
        bytes32                 smgID;                  /// ID of storeman group which user has selected
        uint                    tokenPairID;            /// token pair id on cross chain
        uint[]                  tokenIDs;               /// NFT token Ids
        uint[]                  tokenValues;            /// NFT token values
        uint                    currentChainID;         /// current chain ID
        uint                    tokenPairContractFee;   /// fee of token pair
        uint                    etherTransferGasLimit;  /// exchange token fee
        bytes                   destUserAccount;        /// account of shadow chain, used to receive token
        address                 smgFeeProxy;            /// address of the proxy to store fee for storeman group
    }

    /**
     * @notice Parameters for storeman-initiated NFT minting operations
     * @dev Used when storeman group mints NFTs on the destination chain
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param tokenIDs Array of NFT token IDs to be minted
     * @param tokenValues Array of NFT token values (for ERC1155)
     * @param extData Additional data for storeman operations
     * @param destTokenAccount Destination token contract address
     * @param destUserAccount Destination user account address
     */
    struct RapiditySmgMintNFTParams {
        bytes32                 uniqueID;               /// Rapidity random number
        bytes32                 smgID;                  /// ID of storeman group which user has selected
        uint                    tokenPairID;            /// token pair id on cross chain
        uint[]                  tokenIDs;               /// NFT token Ids
        uint[]                  tokenValues;            /// NFT token values
        bytes                   extData;                /// storeman data
        address                 destTokenAccount;       /// shadow token account
        address                 destUserAccount;        /// account of shadow chain, used to receive token
    }

    /**
     * @notice Parameters for user-initiated NFT burning operations
     * @dev Used when users want to burn their NFTs for cross-chain transfer
     * @param smgID ID of the selected storeman group
     * @param tokenPairID ID of the token pair
     * @param tokenIDs Array of NFT token IDs to be burned
     * @param tokenValues Array of NFT token values (for ERC1155)
     * @param currentChainID ID of the current blockchain
     * @param tokenPairContractFee Fee for the token pair contract
     * @param etherTransferGasLimit Gas limit for ether transfers
     * @param srcTokenAccount Source token contract address
     * @param destUserAccount Destination user account address
     * @param smgFeeProxy Address of the proxy for storing storeman group fees
     */
    struct RapidityUserBurnNFTParams {
        bytes32                 smgID;                  /// ID of storeman group which user has selected
        uint                    tokenPairID;            /// token pair id on cross chain
        uint[]                  tokenIDs;               /// NFT token Ids
        uint[]                  tokenValues;            /// NFT token values
        uint                    currentChainID;         /// current chain ID
        uint                    tokenPairContractFee;   /// fee of token pair
        uint                    etherTransferGasLimit;  /// exchange token fee
        address                 srcTokenAccount;        /// shadow token account
        bytes                   destUserAccount;        /// account of token destination chain, used to receive token
        address                 smgFeeProxy;            /// address of the proxy to store fee for storeman group
    }

    /**
     * @notice Parameters for storeman-initiated NFT release operations
     * @dev Used when storeman group releases NFTs on the original chain
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param tokenIDs Array of NFT token IDs to be released
     * @param tokenValues Array of NFT token values (for ERC1155)
     * @param destTokenAccount Destination token contract address
     * @param destUserAccount Destination user account address
     */
    struct RapiditySmgReleaseNFTParams {
        bytes32                 uniqueID;               /// Rapidity random number
        bytes32                 smgID;                  /// ID of storeman group which user has selected
        uint                    tokenPairID;            /// token pair id on cross chain
        uint[]                  tokenIDs;               /// NFT token Ids
        uint[]                  tokenValues;            /// NFT token values
        address                 destTokenAccount;       /// original token/coin account
        address                 destUserAccount;        /// account of token original chain, used to receive token
    }

    /**
     * @notice Event emitted when NFTs are locked by a user
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param tokenAccount Address of the token contract
     * @param keys Array of event keys
     * @param values Array of event values
     */
    event UserLockNFT(bytes32 indexed smgID, uint indexed tokenPairID, address indexed tokenAccount, string[] keys, bytes[] values);

    /**
     * @notice Event emitted when NFTs are burned by a user
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param tokenAccount Address of the token contract
     * @param keys Array of event keys
     * @param values Array of event values
     */
    event UserBurnNFT(bytes32 indexed smgID, uint indexed tokenPairID, address indexed tokenAccount, string[] keys, bytes[] values);

    /**
     * @notice Event emitted when NFTs are minted by storeman group
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param keys Array of event keys
     * @param values Array of event values
     */
    event SmgMintNFT(bytes32 indexed uniqueID, bytes32 indexed smgID, uint indexed tokenPairID, string[] keys, bytes[] values);

    /**
     * @notice Event emitted when NFTs are released by storeman group
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param keys Array of event keys
     * @param values Array of event values
     */
    event SmgReleaseNFT(bytes32 indexed uniqueID, bytes32 indexed smgID, uint indexed tokenPairID, string[] keys, bytes[] values);

    /**
     * @notice Gets the token contract address and contract fee for a token pair
     * @dev Calculates the appropriate contract fee based on chain IDs and batch size
     * @param storageData Cross storage data
     * @param tokenPairID ID of the token pair
     * @param tokenPairContractFee Fee for the token pair contract
     * @param currentChainID ID of the current blockchain
     * @param batchLength Length of the batch operation
     * @return tokenScAddr Address of the token contract
     * @return contractFee Calculated contract fee
     * Requirements:
     * - Token pair must exist
     * - Current chain ID must be valid
     */
    function getTokenScAddrAndContractFee(CrossTypes.Data storage storageData, uint tokenPairID, uint tokenPairContractFee, uint currentChainID, uint batchLength)
        public
        view
        returns (address, uint)
    {
        ITokenManager tokenManager = storageData.tokenManager;
        uint fromChainID;
        uint toChainID;
        bytes memory fromTokenAccount;
        bytes memory toTokenAccount;
        (fromChainID,fromTokenAccount,toChainID,toTokenAccount) = tokenManager.getTokenPairInfo(tokenPairID);
        require(fromChainID != 0, "Token does not exist");

        uint contractFee = tokenPairContractFee;
        address tokenScAddr;
        if (currentChainID == fromChainID) {
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[fromChainID][toChainID];
            }
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[fromChainID][0];
            }
            tokenScAddr = CrossTypes.bytesToAddress(fromTokenAccount);
        } else if (currentChainID == toChainID) {
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[toChainID][fromChainID];
            }
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[toChainID][0];
            }
            tokenScAddr = CrossTypes.bytesToAddress(toTokenAccount);
        } else {
            require(false, "Invalid token pair");
        }
        if (contractFee > 0) {
            contractFee = contractFee.mul(9 + batchLength).div(10);
        }
        return (tokenScAddr, contractFee);
    }

    /**
     * @notice Locks NFTs for cross-chain transfer
     * @dev Handles the locking of ERC721 and ERC1155 tokens
     * @param storageData Cross storage data
     * @param params Parameters for the NFT locking operation
     * Requirements:
     * - NFT type must be valid (ERC721 or ERC1155)
     * - User must have sufficient balance
     * - Contract must have approval to transfer tokens
     */
    function userLockNFT(CrossTypes.Data storage storageData, RapidityUserLockNFTParams memory params)
        public
    {
        address tokenScAddr;
        uint contractFee;
        (tokenScAddr, contractFee) = getTokenScAddrAndContractFee(storageData, params.tokenPairID, params.tokenPairContractFee, params.currentChainID, params.tokenIDs.length);

        if (contractFee > 0) {
            EtherTransfer.sendValue(payable(params.smgFeeProxy), contractFee, params.etherTransferGasLimit);
        }

        uint left = (msg.value).sub(contractFee);

        uint8 tokenCrossType = storageData.tokenManager.mapTokenPairType(params.tokenPairID);
        if (tokenCrossType == uint8(TokenCrossType.ERC721)) {
            for(uint idx = 0; idx < params.tokenIDs.length; ++idx) {
                IERC721(tokenScAddr).safeTransferFrom(msg.sender, address(this), params.tokenIDs[idx], "");
            }
        } else if(tokenCrossType == uint8(TokenCrossType.ERC1155)) {
            IERC1155(tokenScAddr).safeBatchTransferFrom(msg.sender, address(this), params.tokenIDs, params.tokenValues, "");
        }
        else{
            require(false, "Invalid NFT type");
        }

        if (left != 0) {
            EtherTransfer.sendValue(payable(msg.sender), left, params.etherTransferGasLimit);
        }

        string[] memory keys = new string[](4);
        bytes[] memory values = new bytes[](4);

        keys[0] = "tokenIDs:uint256[]";
        values[0] = abi.encode(params.tokenIDs);

        keys[1] = "tokenValues:uint256[]";
        values[1] = abi.encode(params.tokenValues);

        keys[2] = "userAccount:bytes";
        values[2] = params.destUserAccount;

        keys[3] = "contractFee:uint256";
        values[3] = abi.encodePacked(contractFee);

        emit UserLockNFT(params.smgID, params.tokenPairID, tokenScAddr, keys, values);
    }

    /**
     * @notice Burns NFTs for cross-chain transfer
     * @dev Handles the burning of ERC721 and ERC1155 tokens
     * @param storageData Cross storage data
     * @param params Parameters for the NFT burning operation
     * Requirements:
     * - NFT type must be valid (ERC721 or ERC1155)
     * - User must have sufficient balance
     * - Contract must have approval to burn tokens
     */
    function userBurnNFT(CrossTypes.Data storage storageData, RapidityUserBurnNFTParams memory params)
        public
    {
        address tokenScAddr;
        uint contractFee;
        (tokenScAddr, contractFee) = getTokenScAddrAndContractFee(storageData, params.tokenPairID, params.tokenPairContractFee, params.currentChainID, params.tokenIDs.length);

        ITokenManager tokenManager = storageData.tokenManager;
        uint8 tokenCrossType = tokenManager.mapTokenPairType(params.tokenPairID);
        require((tokenCrossType == uint8(TokenCrossType.ERC721) || tokenCrossType == uint8(TokenCrossType.ERC1155)), "Invalid NFT type");
        ITokenManager(tokenManager).burnNFT(uint(tokenCrossType), tokenScAddr, msg.sender, params.tokenIDs, params.tokenValues);

        if (contractFee > 0) {
            EtherTransfer.sendValue(payable(params.smgFeeProxy), contractFee, params.etherTransferGasLimit);
        }

        uint left = (msg.value).sub(contractFee);
        if (left != 0) {
            EtherTransfer.sendValue(payable(msg.sender), left, params.etherTransferGasLimit);
        }

        string[] memory keys = new string[](4);
        bytes[] memory values = new bytes[](4);

        keys[0] = "tokenIDs:uint256[]";
        values[0] = abi.encode(params.tokenIDs);

        keys[1] = "tokenValues:uint256[]";
        values[1] = abi.encode(params.tokenValues);

        keys[2] = "userAccount:bytes";
        values[2] = params.destUserAccount;

        keys[3] = "contractFee:uint256";
        values[3] = abi.encodePacked(contractFee);
        emit UserBurnNFT(params.smgID, params.tokenPairID, tokenScAddr, keys, values);
    }

    /**
     * @notice Mints NFTs on the destination chain
     * @dev Handles the minting of ERC721 and ERC1155 tokens
     * @param storageData Cross storage data
     * @param params Parameters for the NFT minting operation
     * Requirements:
     * - NFT type must be valid (ERC721 or ERC1155)
     * - Storeman group must have permission to mint
     */
    function smgMintNFT(CrossTypes.Data storage storageData, RapiditySmgMintNFTParams memory params)
        public
    {
        storageData.rapidityTxData.addRapidityTx(params.uniqueID);

        ITokenManager tokenManager = storageData.tokenManager;
        uint8 tokenCrossType = tokenManager.mapTokenPairType(params.tokenPairID);
        require((tokenCrossType == uint8(TokenCrossType.ERC721) || tokenCrossType == uint8(TokenCrossType.ERC1155)), "Invalid NFT type");
        ITokenManager(tokenManager).mintNFT(uint(tokenCrossType), params.destTokenAccount, params.destUserAccount, params.tokenIDs, params.tokenValues, params.extData);

        string[] memory keys = new string[](5);
        bytes[] memory values = new bytes[](5);

        keys[0] = "tokenIDs:uint256[]";
        values[0] = abi.encode(params.tokenIDs);

        keys[1] = "tokenValues:uint256[]";
        values[1] = abi.encode(params.tokenValues);

        keys[2] = "tokenAccount:address";
        values[2] = abi.encodePacked(params.destTokenAccount);

        keys[3] = "userAccount:address";
        values[3] = abi.encodePacked(params.destUserAccount);

        keys[4] = "extData:bytes";
        values[4] = params.extData;

        emit SmgMintNFT(params.uniqueID, params.smgID, params.tokenPairID, keys, values);
    }

    /**
     * @notice Releases NFTs on the original chain
     * @dev Handles the release of ERC721 and ERC1155 tokens
     * @param storageData Cross storage data
     * @param params Parameters for the NFT release operation
     * Requirements:
     * - NFT type must be valid (ERC721 or ERC1155)
     * - Storeman group must have permission to release
     */
    function smgReleaseNFT(CrossTypes.Data storage storageData, RapiditySmgReleaseNFTParams memory params)
        public
    {
        storageData.rapidityTxData.addRapidityTx(params.uniqueID);

        uint8 tokenCrossType = storageData.tokenManager.mapTokenPairType(params.tokenPairID);
        if (tokenCrossType == uint8(TokenCrossType.ERC721)) {
            for(uint idx = 0; idx < params.tokenIDs.length; ++idx) {
                IERC721(params.destTokenAccount).safeTransferFrom(address(this), params.destUserAccount, params.tokenIDs[idx], "");
            }
        }
        else if(tokenCrossType == uint8(TokenCrossType.ERC1155)) {
            IERC1155(params.destTokenAccount).safeBatchTransferFrom(address(this), params.destUserAccount, params.tokenIDs, params.tokenValues, "");
        }
        else {
            require(false, "Invalid NFT type");
        }

        string[] memory keys = new string[](4);
        bytes[] memory values = new bytes[](4);

        keys[0] = "tokenIDs:uint256[]";
        values[0] = abi.encode(params.tokenIDs);

        keys[1] = "tokenValues:uint256[]";
        values[1] = abi.encode(params.tokenValues);

        keys[2] = "tokenAccount:address";
        values[2] = abi.encodePacked(params.destTokenAccount);

        keys[3] = "userAccount:address";
        values[3] = abi.encodePacked(params.destUserAccount);

        emit SmgReleaseNFT(params.uniqueID, params.smgID, params.tokenPairID, keys, values);
    }
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

import "./RapidityTxLib.sol";
import "./CrossTypes.sol";
import "../../interfaces/ITokenManager.sol";
import "../../interfaces/IRC20Protocol.sol";
import "./EtherTransfer.sol";

/**
 * @title RapidityLibV4
 * @dev Library for managing rapid cross-chain token transfers
 * This library provides functionality for:
 * - Token locking and unlocking
 * - Token minting and burning
 * - Cross-chain token transfer management
 */
library RapidityLibV4 {
    using SafeMath for uint;
    using RapidityTxLib for RapidityTxLib.Data;

    /**
     * @notice Enumeration of supported token types for cross-chain operations
     * @dev Defines the types of tokens that can be transferred across chains
     */
    enum TokenCrossType {ERC20, ERC721, ERC1155}

    /**
     * @notice Parameters for user-initiated token locking operations
     * @dev Used when users want to lock their tokens for cross-chain transfer
     * @param smgID ID of the selected storeman group
     * @param tokenPairID ID of the token pair for cross-chain transfer
     * @param value Amount of tokens to transfer
     * @param currentChainID ID of the current blockchain
     * @param tokenPairContractFee Fee for the token pair contract
     * @param etherTransferGasLimit Gas limit for ether transfers
     * @param destUserAccount Destination user account address
     * @param smgFeeProxy Address of the proxy for storing storeman group fees
     */
    struct RapidityUserLockParams {
        bytes32 smgID;
        uint tokenPairID;
        uint value;
        uint currentChainID;
        uint tokenPairContractFee;
        uint etherTransferGasLimit;
        bytes destUserAccount;
        address smgFeeProxy;
    }

    /**
     * @notice Parameters for storeman-initiated token minting operations
     * @dev Used when storeman group mints tokens on the destination chain
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param value Amount of tokens to mint
     * @param fee Transaction fee
     * @param destTokenAccount Destination token contract address
     * @param destUserAccount Destination user account address
     * @param smgFeeProxy Address of the proxy for storing storeman group fees
     */
    struct RapiditySmgMintParams {
        bytes32 uniqueID;
        bytes32 smgID;
        uint tokenPairID;
        uint value;
        uint fee;
        address destTokenAccount;
        address destUserAccount;
        address smgFeeProxy;
    }

    /**
     * @notice Parameters for user-initiated token burning operations
     * @dev Used when users want to burn their tokens for cross-chain transfer
     * @param smgID ID of the selected storeman group
     * @param tokenPairID ID of the token pair
     * @param value Amount of tokens to burn
     * @param currentChainID ID of the current blockchain
     * @param fee Transaction fee
     * @param tokenPairContractFee Fee for the token pair contract
     * @param etherTransferGasLimit Gas limit for ether transfers
     * @param srcTokenAccount Source token contract address
     * @param destUserAccount Destination user account address
     * @param smgFeeProxy Address of the proxy for storing storeman group fees
     */
    struct RapidityUserBurnParams {
        bytes32 smgID;
        uint tokenPairID;
        uint value;
        uint currentChainID;
        uint fee;
        uint tokenPairContractFee;
        uint etherTransferGasLimit;
        address srcTokenAccount;
        bytes destUserAccount;
        address smgFeeProxy;
    }

    /**
     * @notice Parameters for storeman-initiated token release operations
     * @dev Used when storeman group releases tokens on the original chain
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param value Amount of tokens to release
     * @param fee Transaction fee
     * @param etherTransferGasLimit Gas limit for ether transfers
     * @param destTokenAccount Destination token contract address
     * @param destUserAccount Destination user account address
     * @param smgFeeProxy Address of the proxy for storing storeman group fees
     */
    struct RapiditySmgReleaseParams {
        bytes32 uniqueID;
        bytes32 smgID;
        uint tokenPairID;
        uint value;
        uint fee;
        uint etherTransferGasLimit;
        address destTokenAccount;
        address destUserAccount;
        address smgFeeProxy;
    }

    /**
     * @notice Event emitted when tokens are locked by a user
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param tokenAccount Address of the token contract
     * @param value Amount of tokens locked
     * @param contractFee Contract fee charged
     * @param userAccount Destination user account address
     */
    event UserLockLogger(bytes32 indexed smgID, uint indexed tokenPairID, address indexed tokenAccount, uint value, uint contractFee, bytes userAccount);

    /**
     * @notice Event emitted when tokens are burned by a user
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param tokenAccount Address of the token contract
     * @param value Amount of tokens burned
     * @param contractFee Contract fee charged
     * @param fee Transaction fee charged
     * @param userAccount Destination user account address
     */
    event UserBurnLogger(bytes32 indexed smgID, uint indexed tokenPairID, address indexed tokenAccount, uint value, uint contractFee, uint fee, bytes userAccount);

    /**
     * @notice Event emitted when tokens are minted by storeman group
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param value Amount of tokens minted
     * @param tokenAccount Address of the token contract
     * @param userAccount Destination user account address
     */
    event SmgMintLogger(bytes32 indexed uniqueID, bytes32 indexed smgID, uint indexed tokenPairID, uint value, address tokenAccount, address userAccount);

    /**
     * @notice Event emitted when tokens are minted by storeman group (with additional data)
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param keys Array of event keys
     * @param values Array of event values
     */
    event SmgMint(bytes32 indexed uniqueID, bytes32 indexed smgID, uint indexed tokenPairID, string[] keys, bytes[] values);

    /**
     * @notice Event emitted when tokens are released by storeman group
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param value Amount of tokens released
     * @param tokenAccount Address of the token contract
     * @param userAccount Destination user account address
     */
    event SmgReleaseLogger(bytes32 indexed uniqueID, bytes32 indexed smgID, uint indexed tokenPairID, uint value, address tokenAccount, address userAccount);

    /**
     * @notice Event emitted when tokens are released by storeman group (with additional data)
     * @param uniqueID Unique identifier for the rapidity transaction
     * @param smgID ID of the storeman group
     * @param tokenPairID ID of the token pair
     * @param keys Array of event keys
     * @param values Array of event values
     */
    event SmgRelease(bytes32 indexed uniqueID, bytes32 indexed smgID, uint indexed tokenPairID, string[] keys, bytes[] values);

    /**
     * @notice Locks tokens for cross-chain transfer
     * @dev Handles the locking of ERC20 tokens
     * @param storageData Cross storage data
     * @param params Parameters for the token locking operation
     * Requirements:
     * - Token type must be ERC20
     * - User must have sufficient balance
     * - Contract must have approval to transfer tokens
     */
    function userLock(CrossTypes.Data storage storageData, RapidityUserLockParams memory params)
    public
    {
        ITokenManager tokenManager = storageData.tokenManager;
        uint fromChainID;
        uint toChainID;
        bytes memory fromTokenAccount;
        bytes memory toTokenAccount;
        (fromChainID,fromTokenAccount,toChainID,toTokenAccount) = tokenManager.getTokenPairInfo(params.tokenPairID);
        require(fromChainID != 0, "Token does not exist");

        uint contractFee = params.tokenPairContractFee;
        address tokenScAddr;
        if (params.currentChainID == fromChainID) {
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[fromChainID][toChainID];
            }
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[fromChainID][0];
            }
            tokenScAddr = CrossTypes.bytesToAddress(fromTokenAccount);
        } else if (params.currentChainID == toChainID) {
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[toChainID][fromChainID];
            }
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[toChainID][0];
            }
            tokenScAddr = CrossTypes.bytesToAddress(toTokenAccount);
        } else {
            require(false, "Invalid token pair");
        }
        if (contractFee > 0) {
            EtherTransfer.sendValue(payable(params.smgFeeProxy), contractFee, params.etherTransferGasLimit);
        }

        uint left;
        if (tokenScAddr == address(0)) {
            left = (msg.value).sub(params.value).sub(contractFee);
        } else {
            left = (msg.value).sub(contractFee);

            uint8 tokenCrossType = tokenManager.mapTokenPairType(params.tokenPairID);
            require(tokenCrossType == uint8(TokenCrossType.ERC20), "Not support");
            require(CrossTypes.transferFrom(tokenScAddr, msg.sender, address(this), params.value), "Lock token failed");
        }
        if (left != 0) {
            EtherTransfer.sendValue(payable(msg.sender), left, params.etherTransferGasLimit);
        }
        emit UserLockLogger(params.smgID, params.tokenPairID, tokenScAddr, params.value, contractFee, params.destUserAccount);
    }

    /**
     * @notice Burns tokens for cross-chain transfer
     * @dev Handles the burning of ERC20 tokens
     * @param storageData Cross storage data
     * @param params Parameters for the token burning operation
     * Requirements:
     * - Token type must be ERC20
     * - User must have sufficient balance
     * - Contract must have approval to burn tokens
     */
    function userBurn(CrossTypes.Data storage storageData, RapidityUserBurnParams memory params)
    public
    {
        ITokenManager tokenManager = storageData.tokenManager;
        uint fromChainID;
        uint toChainID;
        bytes memory fromTokenAccount;
        bytes memory toTokenAccount;
        (fromChainID,fromTokenAccount,toChainID,toTokenAccount) = tokenManager.getTokenPairInfo(params.tokenPairID);
        require(fromChainID != 0, "Token does not exist");

        uint256 contractFee = params.tokenPairContractFee;
        address tokenScAddr;
        if (params.currentChainID == toChainID) {
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[toChainID][fromChainID];
            }
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[toChainID][0];
            }
            tokenScAddr = CrossTypes.bytesToAddress(toTokenAccount);
        } else if (params.currentChainID == fromChainID) {
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[fromChainID][toChainID];
            }
            if (contractFee == 0) {
                contractFee = storageData.mapContractFee[fromChainID][0];
            }
            tokenScAddr = CrossTypes.bytesToAddress(fromTokenAccount);
        } else {
            require(false, "Invalid token pair");
        }
        require(params.srcTokenAccount == tokenScAddr, "Invalid token account");

        // Reuse variable fromChainID as tokenCrossType; burn token by tokenCrossType
        fromChainID = tokenManager.mapTokenPairType(params.tokenPairID);
        require(fromChainID == uint8(TokenCrossType.ERC20), "Not support");
        require(burnShadowToken(tokenManager, tokenScAddr, msg.sender, params.value), "Burn failed");

        if (contractFee > 0) {
            EtherTransfer.sendValue(payable(params.smgFeeProxy), contractFee, params.etherTransferGasLimit);
        }

        uint left = (msg.value).sub(contractFee);
        if (left != 0) {
            EtherTransfer.sendValue(payable(msg.sender), left, params.etherTransferGasLimit);
        }

        emit UserBurnLogger(params.smgID, params.tokenPairID, tokenScAddr, params.value, contractFee, params.fee, params.destUserAccount);
    }

    /**
     * @notice Mints tokens on the destination chain
     * @dev Handles the minting of ERC20 tokens
     * @param storageData Cross storage data
     * @param params Parameters for the token minting operation
     * Requirements:
     * - Token type must be valid (ERC20)
     * - Storeman group must have permission to mint
     */
    function smgMint(CrossTypes.Data storage storageData, RapiditySmgMintParams memory params)
    public
    {
        storageData.rapidityTxData.addRapidityTx(params.uniqueID);

        ITokenManager tokenManager = storageData.tokenManager;
        uint8 tokenCrossType = tokenManager.mapTokenPairType(params.tokenPairID);
        require(tokenCrossType == uint8(TokenCrossType.ERC20), "Not support");
        if (params.fee > 0) {
            require(mintShadowToken(tokenManager, params.destTokenAccount, params.smgFeeProxy, params.fee), "Mint fee failed");
        }
        require(mintShadowToken(tokenManager, params.destTokenAccount, params.destUserAccount, params.value), "Mint failed");

        string[] memory keys = new string[](4);
        bytes[] memory values = new bytes[](4);
        keys[0] = "value:uint256";
        values[0] = abi.encodePacked(params.value);
        keys[1] = "tokenAccount:address";
        values[1] = abi.encodePacked(params.destTokenAccount);
        keys[2] = "userAccount:address";
        values[2] = abi.encodePacked(params.destUserAccount);
        keys[3] = "fee:uint256";
        values[3] = abi.encodePacked(params.fee);
        emit SmgMint(params.uniqueID, params.smgID, params.tokenPairID, keys, values);
        emit SmgMintLogger(params.uniqueID, params.smgID, params.tokenPairID, params.value, params.destTokenAccount, params.destUserAccount);
    }

    /**
     * @notice Releases tokens on the original chain
     * @dev Handles the release of ERC20 tokens
     * @param storageData Cross storage data
     * @param params Parameters for the token release operation
     * Requirements:
     * - Token type must be ERC20
     * - Storeman group must have permission to release
     */
    function smgRelease(CrossTypes.Data storage storageData, RapiditySmgReleaseParams memory params)
    public
    {
        storageData.rapidityTxData.addRapidityTx(params.uniqueID);

        if (params.destTokenAccount == address(0)) {
            EtherTransfer.sendValue(payable(params.destUserAccount), params.value, params.etherTransferGasLimit);
            if (params.fee > 0) {
                EtherTransfer.sendValue(payable(params.smgFeeProxy), params.fee, params.etherTransferGasLimit);
            }
        } else {
            uint8 tokenCrossType = storageData.tokenManager.mapTokenPairType(params.tokenPairID);
            require(tokenCrossType == uint8(TokenCrossType.ERC20), "Not support");
            if (params.fee > 0) {
                require(CrossTypes.transfer(params.destTokenAccount, params.smgFeeProxy, params.fee), "Transfer token fee failed");
            }
            require(CrossTypes.transfer(params.destTokenAccount, params.destUserAccount, params.value), "Transfer token failed");
        }

        string[] memory keys = new string[](4);
        bytes[] memory values = new bytes[](4);
        keys[0] = "value:uint256";
        values[0] = abi.encodePacked(params.value);
        keys[1] = "tokenAccount:address";
        values[1] = abi.encodePacked(params.destTokenAccount);
        keys[2] = "userAccount:address";
        values[2] = abi.encodePacked(params.destUserAccount);
        keys[3] = "fee:uint256";
        values[3] = abi.encodePacked(params.fee);
        emit SmgRelease(params.uniqueID, params.smgID, params.tokenPairID, keys, values);
        emit SmgReleaseLogger(params.uniqueID, params.smgID, params.tokenPairID, params.value, params.destTokenAccount, params.destUserAccount);
    }

    /**
     * @notice Burns shadow tokens
     * @dev Handles the burning of ERC20 tokens
     * @param tokenManager Token manager contract
     * @param tokenAddress Address of the token contract
     * @param userAccount Address of the user account
     * @param value Amount of tokens to burn
     * @return bool indicating whether the burn was successful
     */
    function burnShadowToken(ITokenManager tokenManager, address tokenAddress, address userAccount, uint value) private returns (bool) {
        uint beforeBalance;
        uint afterBalance;
        beforeBalance = IRC20Protocol(tokenAddress).balanceOf(userAccount);

        tokenManager.burnToken(tokenAddress, userAccount, value);

        afterBalance = IRC20Protocol(tokenAddress).balanceOf(userAccount);
        return afterBalance == beforeBalance.sub(value);
    }

    /**
     * @notice Mints shadow tokens
     * @dev Handles the minting of ERC20 tokens
     * @param tokenManager Token manager contract
     * @param tokenAddress Address of the token contract
     * @param userAccount Address of the user account
     * @param value Amount of tokens to mint
     * @return bool indicating whether the mint was successful
     */
    function mintShadowToken(ITokenManager tokenManager, address tokenAddress, address userAccount, uint value) private returns (bool) {
        uint beforeBalance;
        uint afterBalance;
        beforeBalance = IRC20Protocol(tokenAddress).balanceOf(userAccount);

        tokenManager.mintToken(tokenAddress, userAccount, value);

        afterBalance = IRC20Protocol(tokenAddress).balanceOf(userAccount);
        return afterBalance == beforeBalance.add(value);
    }
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

/**
 * @title RapidityTxLib
 * @dev Library for managing rapid cross-chain transaction status
 * This library provides functionality for:
 * - Tracking transaction status
 * - Managing rapid cross-chain transactions
 */
library RapidityTxLib {

    /**
     * @notice Enumeration of possible transaction statuses
     * @dev Defines the states a rapidity transaction can be in
     * - None: Initial state, transaction not yet processed
     * - Redeemed: Transaction has been completed
     */
    enum TxStatus {None, Redeemed}

    /**
     * @notice Main data structure for rapidity transactions
     * @dev Contains mappings for tracking transaction statuses
     * @param mapTxStatus Mapping of transaction unique IDs to their status
     */
    struct Data {
        mapping(bytes32 => TxStatus) mapTxStatus;
    }

    /**
     * @notice Adds a new rapidity transaction
     * @dev Marks a transaction as redeemed when it is added
     * @param self The storage data structure
     * @param uniqueID Unique identifier for the rapidity transaction
     * Requirements:
     * - Transaction must not already exist
     */
    function addRapidityTx(Data storage self, bytes32 uniqueID)
        internal
    {
        TxStatus status = self.mapTxStatus[uniqueID];
        require(status == TxStatus.None, "Rapidity tx exists");
        self.mapTxStatus[uniqueID] = TxStatus.Redeemed;
    }
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

interface IQuota {
  function userLock(uint tokenId, bytes32 storemanGroupId, uint value) external;
  function userBurn(uint tokenId, bytes32 storemanGroupId, uint value) external;

  function smgRelease(uint tokenId, bytes32 storemanGroupId, uint value) external;
  function smgMint(uint tokenId, bytes32 storemanGroupId, uint value) external;

  function upgrade(bytes32 storemanGroupId) external;

  function transferAsset(bytes32 srcStoremanGroupId, bytes32 dstStoremanGroupId) external;
  function receiveDebt(bytes32 srcStoremanGroupId, bytes32 dstStoremanGroupId) external;

  function getUserMintQuota(uint tokenId, bytes32 storemanGroupId) external view returns (uint);
  function getSmgMintQuota(uint tokenId, bytes32 storemanGroupId) external view returns (uint);

  function getUserBurnQuota(uint tokenId, bytes32 storemanGroupId) external view returns (uint);
  function getSmgBurnQuota(uint tokenId, bytes32 storemanGroupId) external view returns (uint);

  function getAsset(uint tokenId, bytes32 storemanGroupId) external view returns (uint asset, uint asset_receivable, uint asset_payable);
  function getDebt(uint tokenId, bytes32 storemanGroupId) external view returns (uint debt, uint debt_receivable, uint debt_payable);

  function isDebtClean(bytes32 storemanGroupId) external view returns (bool);
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

interface IRC20Protocol {
    function transfer(address, uint) external returns (bool);
    function transferFrom(address, address, uint) external returns (bool);
    function balanceOf(address _owner) external view returns (uint);
}

File 23 of 26 : ISignatureVerifier.sol
// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

interface ISignatureVerifier {
  function verify(
        uint curveId,
        bytes32 signature,
        bytes32 groupKeyX,
        bytes32 groupKeyY,
        bytes32 randomPointX,
        bytes32 randomPointY,
        bytes32 message
    ) external returns (bool);
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

interface IStoremanGroup {
    function getSelectedSmNumber(bytes32 groupId) external view returns(uint number);
    function getStoremanGroupConfig(bytes32 id) external view returns(bytes32 groupId, uint8 status, uint deposit, uint chain1, uint chain2, uint curve1, uint curve2,  bytes memory gpk1, bytes memory gpk2, uint startTime, uint endTime);
    function getDeposit(bytes32 id) external view returns(uint);
    function getStoremanGroupStatus(bytes32 id) external view returns(uint8 status, uint startTime, uint endTime);
    function setGpk(bytes32 groupId, bytes memory gpk1, bytes memory gpk2) external;
    function setInvalidSm(bytes32 groupId, uint[] memory indexs, uint8[] memory slashTypes) external returns(bool isContinue);
    function getThresholdByGrpId(bytes32 groupId) external view returns (uint);
    function getSelectedSmInfo(bytes32 groupId, uint index) external view returns(address wkAddr, bytes memory PK, bytes memory enodeId);
    function recordSmSlash(address wk) external;
}

// SPDX-License-Identifier: MIT

/*

  Copyright 2023 Wanchain Foundation.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

//                            _           _           _
//  __      ____ _ _ __   ___| |__   __ _(_)_ __   __| | _____   __
//  \ \ /\ / / _` | '_ \ / __| '_ \ / _` | | '_ \@/ _` |/ _ \ \ / /
//   \ V  V / (_| | | | | (__| | | | (_| | | | | | (_| |  __/\ V /
//    \_/\_/ \__,_|_| |_|\___|_| |_|\__,_|_|_| |_|\__,_|\___| \_/
//
//

pragma solidity ^0.8.18;

interface ITokenManager {
    function getTokenPairInfo(uint id) external view
      returns (uint origChainID, bytes memory tokenOrigAccount, uint shadowChainID, bytes memory tokenShadowAccount);

    function getTokenPairInfoSlim(uint id) external view
      returns (uint origChainID, bytes memory tokenOrigAccount, uint shadowChainID);

    function getAncestorInfo(uint id) external view
      returns (bytes memory account, string memory name, string memory symbol, uint8 decimals, uint chainId);

    function mintToken(address tokenAddress, address to, uint value) external;

    function burnToken(address tokenAddress, address from, uint value) external;

    function mapTokenPairType(uint tokenPairID) external view
      returns (uint8 tokenPairType);

    // erc1155
    function mintNFT(uint tokenCrossType, address tokenAddress, address to, uint[] calldata ids, uint[] calldata values, bytes calldata data) external;
    function burnNFT(uint tokenCrossType, address tokenAddress, address from, uint[] calldata ids, uint[] calldata values) external;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

/**
 * @title BasicStorageLib
 * @dev Library for basic storage operations with support for multiple data types
 * This library provides a structured approach to contract storage with a two-level key system
 * 
 * Key features:
 * - Support for multiple data types (uint, bool, address, bytes, string)
 * - Two-level key storage system for flexible data organization
 * - CRUD operations for each data type with consistent interface
 * - Gas-efficient storage patterns
 * 
 * @custom:security
 * - Internal access only to prevent unauthorized storage manipulation
 * - Safe storage operations with proper type enforcement
 * - Type-specific operations to prevent type confusion
 * - Consistent interface reduces likelihood of implementation errors
 * 
 * @custom:usage
 * - Used as a foundation for complex contract storage patterns
 * - Enables modular and organized data storage in contracts
 * - Simplifies storage access with standardized methods
 * - Perfect for contracts with diverse data storage needs
 */
library BasicStorageLib {

    /**
     * @dev Structure for storing uint values
     * Provides a two-level nested mapping for uint storage
     * 
     * @custom:usage
     * - Stores uint values with two-level key system for hierarchical data
     * - Used for numeric data storage such as balances, timestamps, amounts
     * - Primary key often represents a category, while innerKey represents specific item
     * - Essential for tracking numeric values in complex systems
     */
    struct UintData {
        mapping(bytes => mapping(bytes => uint))           _storage;
    }

    /**
     * @dev Structure for storing boolean values
     * Provides a two-level nested mapping for boolean storage
     * 
     * @custom:usage
     * - Stores boolean values with two-level key system for hierarchical data
     * - Used for flag and state storage such as activation status or permissions
     * - Efficient for representing binary states (true/false)
     * - Perfect for access control and feature toggles
     */
    struct BoolData {
        mapping(bytes => mapping(bytes => bool))           _storage;
    }

    /**
     * @dev Structure for storing address values
     * Provides a two-level nested mapping for address storage
     * 
     * @custom:usage
     * - Stores address values with two-level key system for hierarchical data
     * - Used for contract and account address storage
     * - Essential for tracking ownership, relationships between entities
     * - Enables role-based systems and contract registries
     */
    struct AddressData {
        mapping(bytes => mapping(bytes => address))        _storage;
    }

    /**
     * @dev Structure for storing bytes values
     * Provides a two-level nested mapping for bytes storage
     * 
     * @custom:usage
     * - Stores bytes values with two-level key system for hierarchical data
     * - Used for raw data storage such as cryptographic proofs, signatures
     * - Perfect for storing variable-length binary data
     * - Enables storage of complex serialized structures
     */
    struct BytesData {
        mapping(bytes => mapping(bytes => bytes))          _storage;
    }

    /**
     * @dev Structure for storing string values
     * Provides a two-level nested mapping for string storage
     * 
     * @custom:usage
     * - Stores string values with two-level key system for hierarchical data
     * - Used for text data storage such as names, descriptions, metadata
     * - Human-readable information storage
     * - Suitable for configuration parameters and user-facing content
     */
    struct StringData {
        mapping(bytes => mapping(bytes => string))         _storage;
    }

    /**
     * @dev Set uint value in storage
     * Assigns a uint value to a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization (e.g., user ID, token type)
     * @param innerKey Secondary key for specific attribute (e.g., balance, timestamp)
     * @param value Unsigned integer value to store
     * 
     * @custom:effects
     * - Updates storage with new value, overwriting any existing value
     * - Gas usage scales with key sizes, not with value size
     * - Optimized for single-slot storage operations
     */
    function setStorage(UintData storage self, bytes memory key, bytes memory innerKey, uint value) internal {
        self._storage[key][innerKey] = value;
    }

    /**
     * @dev Get uint value from storage
     * Retrieves a uint value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific attribute
     * @return Stored uint value, or 0 if no value has been set
     * 
     * @custom:effects
     * - Read-only operation that doesn't modify state
     * - Returns default value (0) if entry doesn't exist
     * - Gas cost is constant regardless of value stored
     */
    function getStorage(UintData storage self, bytes memory key, bytes memory innerKey) internal view returns (uint) {
        return self._storage[key][innerKey];
    }

    /**
     * @dev Delete uint value from storage
     * Removes a uint value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific attribute
     * 
     * @custom:effects
     * - Removes value from storage, setting it to default value (0)
     * - Gas refund is provided when clearing storage to zero
     * - Frees up storage space, potentially reducing contract storage costs
     */
    function delStorage(UintData storage self, bytes memory key, bytes memory innerKey) internal {
        delete self._storage[key][innerKey];
    }

    /**
     * @dev Set boolean value in storage
     * Assigns a boolean value to a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization (e.g., feature name, user ID)
     * @param innerKey Secondary key for specific flag (e.g., active, approved)
     * @param value Boolean value to store
     * 
     * @custom:effects
     * - Updates storage with new value, overwriting any existing value
     * - Gas efficient for storing binary state information
     * - Packs values efficiently in storage
     */
    function setStorage(BoolData storage self, bytes memory key, bytes memory innerKey, bool value) internal {
        self._storage[key][innerKey] = value;
    }

    /**
     * @dev Get boolean value from storage
     * Retrieves a boolean value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific flag
     * @return Stored boolean value, or false if no value has been set
     * 
     * @custom:effects
     * - Read-only operation that doesn't modify state
     * - Returns default value (false) if entry doesn't exist
     * - Gas efficient for checking state conditions
     */
    function getStorage(BoolData storage self, bytes memory key, bytes memory innerKey) internal view returns (bool) {
        return self._storage[key][innerKey];
    }

    /**
     * @dev Delete boolean value from storage
     * Removes a boolean value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific flag
     * 
     * @custom:effects
     * - Removes value from storage, setting it to default value (false)
     * - Gas refund is provided when clearing storage to zero
     * - Particularly efficient for boolean values
     */
    function delStorage(BoolData storage self, bytes memory key, bytes memory innerKey) internal {
        delete self._storage[key][innerKey];
    }

    /**
     * @dev Set address value in storage
     * Assigns an address value to a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization (e.g., role name, contract type)
     * @param innerKey Secondary key for specific relationship (e.g., owner, delegate)
     * @param value Ethereum address to store
     * 
     * @custom:effects
     * - Updates storage with new address, overwriting any existing value
     * - Stores full 20-byte Ethereum addresses
     * - Critical for tracking contract relationships and ownership
     */
    function setStorage(AddressData storage self, bytes memory key, bytes memory innerKey, address value) internal {
        self._storage[key][innerKey] = value;
    }

    /**
     * @dev Get address value from storage
     * Retrieves an address value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific relationship
     * @return Stored address value, or address(0) if no value has been set
     * 
     * @custom:effects
     * - Read-only operation that doesn't modify state
     * - Returns default value (address(0)) if entry doesn't exist
     * - Used for permission checks and relationship verification
     */
    function getStorage(AddressData storage self, bytes memory key, bytes memory innerKey) internal view returns (address) {
        return self._storage[key][innerKey];
    }

    /**
     * @dev Delete address value from storage
     * Removes an address value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific relationship
     * 
     * @custom:effects
     * - Removes value from storage, setting it to default value (address(0))
     * - Gas refund is provided when clearing storage to zero
     * - Important for revoking permissions or updating relationships
     */
    function delStorage(AddressData storage self, bytes memory key, bytes memory innerKey) internal {
        delete self._storage[key][innerKey];
    }

    /**
     * @dev Set bytes value in storage
     * Assigns a bytes value to a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization (e.g., data type, record ID)
     * @param innerKey Secondary key for specific data (e.g., signature, hash)
     * @param value Bytes data to store
     * 
     * @custom:effects
     * - Updates storage with new bytes data, overwriting any existing value
     * - Dynamically sized data is stored with length prefix
     * - Gas cost scales with the size of the bytes array
     * - Suitable for arbitrary binary data storage
     */
    function setStorage(BytesData storage self, bytes memory key, bytes memory innerKey, bytes memory value) internal {
        self._storage[key][innerKey] = value;
    }

    /**
     * @dev Get bytes value from storage
     * Retrieves a bytes value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific data
     * @return Stored bytes value, or empty bytes if no value has been set
     * 
     * @custom:effects
     * - Read-only operation that doesn't modify state
     * - Returns default value (empty bytes) if entry doesn't exist
     * - Gas cost scales with the size of the retrieved data
     * - Used for retrieving serialized data, proofs, or signatures
     */
    function getStorage(BytesData storage self, bytes memory key, bytes memory innerKey) internal view returns (bytes memory) {
        return self._storage[key][innerKey];
    }

    /**
     * @dev Delete bytes value from storage
     * Removes a bytes value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific data
     * 
     * @custom:effects
     * - Removes value from storage, setting it to default value (empty bytes)
     * - Gas refund is provided when clearing storage
     * - More gas efficient for larger data due to storage refunds
     * - Complete removal of variable-length data
     */
    function delStorage(BytesData storage self, bytes memory key, bytes memory innerKey) internal {
        delete self._storage[key][innerKey];
    }

    /**
     * @dev Set string value in storage
     * Assigns a string value to a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization (e.g., metadata type, record ID)
     * @param innerKey Secondary key for specific text (e.g., name, description)
     * @param value String data to store
     * 
     * @custom:effects
     * - Updates storage with new string, overwriting any existing value
     * - Strings are stored as UTF-8 encoded bytes with length prefix
     * - Gas cost scales with the length of the string
     * - Ideal for human-readable text storage
     */
    function setStorage(StringData storage self, bytes memory key, bytes memory innerKey, string memory value) internal {
        self._storage[key][innerKey] = value;
    }

    /**
     * @dev Get string value from storage
     * Retrieves a string value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific text
     * @return Stored string value, or empty string if no value has been set
     * 
     * @custom:effects
     * - Read-only operation that doesn't modify state
     * - Returns default value (empty string) if entry doesn't exist
     * - Gas cost scales with the length of the retrieved string
     * - Used for retrieving human-readable configuration and metadata
     */
    function getStorage(StringData storage self, bytes memory key, bytes memory innerKey) internal view returns (string memory) {
        return self._storage[key][innerKey];
    }

    /**
     * @dev Delete string value from storage
     * Removes a string value from a specific key pair in storage
     * 
     * @param self Storage structure reference
     * @param key Primary key for categorization
     * @param innerKey Secondary key for specific text
     * 
     * @custom:effects
     * - Removes value from storage, setting it to default value (empty string)
     * - Gas refund is provided when clearing storage
     * - More gas efficient for longer strings due to storage refunds
     * - Complete removal of variable-length text data
     */
    function delStorage(StringData storage self, bytes memory key, bytes memory innerKey) internal {
        delete self._storage[key][innerKey];
    }

}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/crossApproach/CrossDelegateV4.sol": {
      "NFTLibV1": "0x1faa6f2b3c252da20820593d6de5c40ffcaf7655",
      "RapidityLibV4": "0xee91925aacca9b96a77a977ab1dd1688f596858e"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":true,"internalType":"bool","name":"enabled","type":"bool"}],"name":"ConfigAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"bool","name":"enabled","type":"bool"}],"name":"ConfigOperator","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":"adminAccount","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"srcChainID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"destChainID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"contractFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"agentFee","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"contractFee","type":"uint256"}],"name":"SetTokenPairFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"smgID","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"timeStamp","type":"uint256"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"WithdrawHistoryFeeLogger","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"changeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"configAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"configOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentChainID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"key","type":"bytes"},{"internalType":"bytes","name":"innerKey","type":"bytes"}],"name":"delUintValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256","name":"batchLength","type":"uint256"}],"name":"getBatchFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEtherTransferGasLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"srcChainID","type":"uint256"},{"internalType":"uint256","name":"destChainID","type":"uint256"}],"internalType":"struct CrossStorageV2.GetFeesParam","name":"param","type":"tuple"}],"name":"getFee","outputs":[{"components":[{"internalType":"uint256","name":"contractFee","type":"uint256"},{"internalType":"uint256","name":"agentFee","type":"uint256"}],"internalType":"struct CrossStorageV2.GetFeesReturn","name":"fee","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"srcChainID","type":"uint256"},{"internalType":"uint256","name":"destChainID","type":"uint256"}],"internalType":"struct CrossStorageV2.GetFeesParam[]","name":"params","type":"tuple[]"}],"name":"getFees","outputs":[{"components":[{"internalType":"uint256","name":"contractFee","type":"uint256"},{"internalType":"uint256","name":"agentFee","type":"uint256"}],"internalType":"struct CrossStorageV2.GetFeesReturn[]","name":"fees","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxBatchSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPartners","outputs":[{"internalType":"address","name":"tokenManager","type":"address"},{"internalType":"address","name":"smgAdminProxy","type":"address"},{"internalType":"address","name":"smgFeeProxy","type":"address"},{"internalType":"address","name":"quota","type":"address"},{"internalType":"address","name":"sigVerifier","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getStoremanFee","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenPairID","type":"uint256"}],"name":"getTokenPairFee","outputs":[{"internalType":"uint256","name":"contractFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenPairIDs","type":"uint256[]"}],"name":"getTokenPairFees","outputs":[{"internalType":"uint256[]","name":"contractFees","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"key","type":"bytes"},{"internalType":"bytes","name":"innerKey","type":"bytes"}],"name":"getUintValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"halted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"hashFunc","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hashType","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"adminAccount","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainID","type":"uint256"}],"name":"setChainID","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_etherTransferGasLimit","type":"uint256"}],"name":"setEtherTransferGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"srcChainID","type":"uint256"},{"internalType":"uint256","name":"destChainID","type":"uint256"},{"internalType":"uint256","name":"contractFee","type":"uint256"},{"internalType":"uint256","name":"agentFee","type":"uint256"}],"internalType":"struct CrossStorageV2.SetFeesParam","name":"param","type":"tuple"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"srcChainID","type":"uint256"},{"internalType":"uint256","name":"destChainID","type":"uint256"},{"internalType":"uint256","name":"contractFee","type":"uint256"},{"internalType":"uint256","name":"agentFee","type":"uint256"}],"internalType":"struct CrossStorageV2.SetFeesParam[]","name":"params","type":"tuple[]"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"halt","type":"bool"}],"name":"setHalt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hashType","type":"uint256"}],"name":"setHashType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxBatchSize","type":"uint256"}],"name":"setMaxBatchSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenManager","type":"address"},{"internalType":"address","name":"smgAdminProxy","type":"address"},{"internalType":"address","name":"smgFeeProxy","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"sigVerifier","type":"address"}],"name":"setPartners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256","name":"contractFee","type":"uint256"}],"name":"setTokenPairFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256","name":"contractFee","type":"uint256"}],"internalType":"struct CrossStorageV3.SetTokenPairFeesParam[]","name":"params","type":"tuple[]"}],"name":"setTokenPairFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"key","type":"bytes"},{"internalType":"bytes","name":"innerKey","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setUintValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"smgFeeReceiverTimeout","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"uniqueID","type":"bytes32"},{"internalType":"bytes32","name":"smgID","type":"bytes32"},{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"address","name":"tokenAccount","type":"address"},{"internalType":"address","name":"userAccount","type":"address"},{"internalType":"bytes","name":"r","type":"bytes"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"smgMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"uniqueID","type":"bytes32"},{"internalType":"bytes32","name":"smgID","type":"bytes32"},{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256[]","name":"tokenIDs","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenValues","type":"uint256[]"},{"internalType":"bytes","name":"extData","type":"bytes"},{"internalType":"address","name":"tokenAccount","type":"address"},{"internalType":"address","name":"userAccount","type":"address"},{"internalType":"bytes","name":"r","type":"bytes"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"smgMintNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"uniqueID","type":"bytes32"},{"internalType":"bytes32","name":"smgID","type":"bytes32"},{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"address","name":"tokenAccount","type":"address"},{"internalType":"address","name":"userAccount","type":"address"},{"internalType":"bytes","name":"r","type":"bytes"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"smgRelease","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"uniqueID","type":"bytes32"},{"internalType":"bytes32","name":"smgID","type":"bytes32"},{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256[]","name":"tokenIDs","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenValues","type":"uint256[]"},{"internalType":"address","name":"tokenAccount","type":"address"},{"internalType":"address","name":"userAccount","type":"address"},{"internalType":"bytes","name":"r","type":"bytes"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"smgReleaseNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"smgIDs","type":"bytes32[]"}],"name":"smgWithdrawHistoryFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"smgID","type":"bytes32"},{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"address","name":"tokenAccount","type":"address"},{"internalType":"bytes","name":"userAccount","type":"bytes"}],"name":"userBurn","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"smgID","type":"bytes32"},{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256[]","name":"tokenIDs","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenValues","type":"uint256[]"},{"internalType":"address","name":"tokenAccount","type":"address"},{"internalType":"bytes","name":"userAccount","type":"bytes"}],"name":"userBurnNFT","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"smgID","type":"bytes32"},{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"userAccount","type":"bytes"}],"name":"userLock","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"smgID","type":"bytes32"},{"internalType":"uint256","name":"tokenPairID","type":"uint256"},{"internalType":"uint256[]","name":"tokenIDs","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenValues","type":"uint256[]"},{"internalType":"bytes","name":"userAccount","type":"bytes"}],"name":"userLockNFT","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60806040526201fa406011556102586012556014805460ff60a01b1916905534801561002a57600080fd5b506013805433610100026001600160a81b0319909116176001179055614689806100556000396000f3fe60806040526004361061031e5760003560e01c806373e29b0d116101ab578063bbf485ab116100f7578063e7c00f6e11610095578063f0baef521161006f578063f0baef5214610a1a578063f23a6e6114610a3a578063f495438714610a66578063f851a44014610a865761032d565b8063e7c00f6e146109cf578063e92e2c1b146109e4578063ed8d47e6146109fa5761032d565b8063d4ee1d90116100d1578063d4ee1d901461095c578063d52012bc1461097c578063dd27b6cd1461099c578063e02dee8c146109bc5761032d565b8063bbf485ab146108ed578063bc197c811461090d578063be5212a81461093c5761032d565b8063926731ea11610164578063a8b382051161013e578063a8b382051461088d578063b179e1e7146108a3578063b64ed6db146108b9578063b9b8af0b146108cc5761032d565b8063926731ea1461082d578063a3d1e3811461084d578063a6f9dae11461086d5761032d565b806373e29b0d1461072f57806373fa58f51461078657806379ba5097146107a65780638061babf146107bb5780638da5cb5b146107db57806390116916146108005761032d565b8063392a62b91161026a57806354c0de4a116102235780636d70f7ae116101fd5780636d70f7ae1461069d578063704b6c02146106cd578063715018a6146106ed578063715f250c146107025761032d565b806354c0de4a1461062b5780635c60da1b1461064b5780635dd939ba1461067d5761032d565b8063392a62b91461056b57806341ff1bcc146105985780634cc7125b146105ab5780634fb2e45d146105cb57806350cc6707146105eb578063536686a91461060b5761032d565b8063213a2596116102d75780632b26a6bf116102b15780632b26a6bf146104de5780632e96be19146104fe57806333b57a401461051e578063361b31bf1461053e5761032d565b8063213a25961461046b57806324d7806c1461048b578063257011b6146104cb5761032d565b806301f4d28d146103355780630a72b1ab1461035d5780630d3adbac1461037d5780630ec61b7914610393578063150b7a0214610407578063159982f01461044b5761032d565b3661032d5761032b610aa6565b005b61032b610aa6565b34801561034157600080fd5b5061034a610b30565b6040519081526020015b60405180910390f35b34801561036957600080fd5b5061032b61037836600461311d565b610b4a565b34801561038957600080fd5b5061034a601b5481565b34801561039f57600080fd5b506103fa6103ae36600461318e565b604080518082018252600080825260208083018281528535808452600f835285842096830135808552968352858420548552835260108252848320958352949052919091205490915290565b60405161035491906131a6565b34801561041357600080fd5b50610432610422366004613280565b630a85bd0160e11b949350505050565b6040516001600160e01b03199091168152602001610354565b34801561045757600080fd5b5061034a6104663660046132eb565b610c3e565b34801561047757600080fd5b5061032b610486366004613372565b610cb1565b34801561049757600080fd5b506104bb6104a63660046133b3565b601c6020526000908152604090205460ff1681565b6040519015158152602001610354565b61032b6104d9366004613411565b610e07565b3480156104ea57600080fd5b5061032b6104f9366004613471565b61103f565b34801561050a57600080fd5b5061032b610519366004613471565b6110a2565b34801561052a57600080fd5b5061032b610539366004613498565b6110d6565b34801561054a57600080fd5b5061055e610559366004613372565b611188565b60405161035491906134d1565b34801561057757600080fd5b5061034a610586366004613471565b60009081526018602052604090205490565b61032b6105a6366004613528565b611300565b3480156105b757600080fd5b5061032b6105c63660046135a4565b61154f565b3480156105d757600080fd5b5061032b6105e63660046133b3565b611628565b3480156105f757600080fd5b5061032b61060636600461360f565b611714565b34801561061757600080fd5b5061032b6106263660046136f9565b611958565b34801561063757600080fd5b5061032b61064636600461372e565b611ad3565b34801561065757600080fd5b506015546001600160a01b03165b6040516001600160a01b039091168152602001610354565b34801561068957600080fd5b5061032b610698366004613498565b611bcc565b3480156106a957600080fd5b506104bb6106b83660046133b3565b601d6020526000908152604090205460ff1681565b3480156106d957600080fd5b5061032b6106e83660046133b3565b611c4f565b3480156106f957600080fd5b5061032b611cd2565b34801561070e57600080fd5b5061034a61071d366004613471565b6000908152600e602052604090205490565b34801561073b57600080fd5b50600a54600b54600c54600954600d54604080516001600160a01b0396871681529486166020860152928516928401929092528316606083015291909116608082015260a001610354565b34801561079257600080fd5b5061032b6107a136600461360f565b611d14565b3480156107b257600080fd5b5061032b611e1b565b3480156107c757600080fd5b5061034a6107d63660046135a4565b611e59565b3480156107e757600080fd5b506013546106659061010090046001600160a01b031681565b34801561080c57600080fd5b5061082061081b3660046136f9565b611ed9565b604051610354919061377b565b34801561083957600080fd5b5061032b61084836600461378e565b611f83565b34801561085957600080fd5b5061032b610868366004613880565b612058565b34801561087957600080fd5b5061032b6108883660046133b3565b612180565b34801561089957600080fd5b5061034a60115481565b3480156108af57600080fd5b5061034a60165481565b61032b6108c7366004613950565b6121d1565b3480156108d857600080fd5b506014546104bb90600160a01b900460ff1681565b3480156108f957600080fd5b5061032b6109083660046139eb565b61246b565b34801561091957600080fd5b50610432610928366004613ae1565b63bc197c8160e01b98975050505050505050565b34801561094857600080fd5b5061034a610957366004613b9f565b61258e565b34801561096857600080fd5b50601454610665906001600160a01b031681565b34801561098857600080fd5b5061032b610997366004613bc1565b612633565b3480156109a857600080fd5b5061032b6109b7366004613471565b612870565b61032b6109ca366004613c35565b6128d3565b3480156109db57600080fd5b5061034a612b33565b3480156109f057600080fd5b5061034a60125481565b348015610a0657600080fd5b5061032b610a15366004613471565b612b4c565b348015610a2657600080fd5b5061032b610a35366004613b9f565b612bbd565b348015610a4657600080fd5b50610432610a55366004613ce3565b63f23a6e6160e01b95945050505050565b348015610a7257600080fd5b5061032b610a81366004613d4b565b612c86565b348015610a9257600080fd5b50601754610665906001600160a01b031681565b6015546001600160a01b031680610b045760405162461bcd60e51b815260206004820152601f60248201527f696d706c656d656e746174696f6e20636f6e7472616374206e6f74207365740060448201526064015b60405180910390fd5b60405136600082376000803683855af43d806000843e818015610b25578184f35b8184fd5b5050505050565b6000601a54600003610b4357506108fc90565b50601a5490565b60135461010090046001600160a01b03163314610b795760405162461bcd60e51b8152600401610afb90613d68565b6001600160a01b03851615801590610b9957506001600160a01b03841615155b8015610bad57506001600160a01b03811615155b610bf05760405162461bcd60e51b815260206004820152601460248201527314185c985b595d195c881a5cc81a5b9d985b1a5960621b6044820152606401610afb565b600b80546001600160a01b03199081166001600160a01b0396871617909155600a8054821696861696909617909555600c805486169385169390931790925550600d80549093169116179055565b6000601b54600103610c565750805160209091012090565b600282604051610c669190613daf565b602060405180830381855afa158015610c83573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610ca69190613dcb565b92915050565b919050565b336000908152601d602052604090205460ff1680610cde5750336000908152601c602052604090205460ff165b80610cf357506017546001600160a01b031633145b80610d0d575060135461010090046001600160a01b031633145b610d295760405162461bcd60e51b8152600401610afb90613de4565b60005b81811015610e0257828282818110610d4657610d46613e0a565b9050604002016020013560186000858585818110610d6657610d66613e0a565b90506040020160000135815260200190815260200160002081905550828282818110610d9457610d94613e0a565b905060400201600001357fdfa3e1a2556a2caf7af0a1cb98a9eed056ae433c4e109e3398edff9863d45bf5848484818110610dd157610dd1613e0a565b90506040020160200135604051610dea91815260200190565b60405180910390a2610dfb81613e36565b9050610d2c565b505050565b601454600160a01b900460ff1615610e315760405162461bcd60e51b8152600401610afb90613e4f565b60135460ff16610e535760405162461bcd60e51b8152600401610afb90613e86565b6013805460ff19169055600b54604051634af46b4560e11b8152600481018790528691600091829182916001600160a01b03909116906395e8d68a90602401606060405180830381865afa158015610eaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed39190613ece565b9194509250905060ff83166005148015610eed5750814210155b8015610ef95750804211155b610f155760405162461bcd60e51b8152600401610afb90613f03565b6000610f1f612cd3565b905060006040518061010001604052808c81526020018b81526020018a81526020016016548152602001601860008d8152602001908152602001600020548152602001610f6a610b30565b815260200189898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b038416602090910152604051638fd59dc760e01b815290915073ee91925aacca9b96a77a977ab1dd1688f596858e90638fd59dc790610ff5906005908590600401613f58565b60006040518083038186803b15801561100d57600080fd5b505af4158015611021573d6000803e3d6000fd5b50506013805460ff1916600117905550505050505050505050505050565b336000908152601c602052604090205460ff168061106757506017546001600160a01b031633145b80611081575060135461010090046001600160a01b031633145b61109d5760405162461bcd60e51b8152600401610afb90613fdc565b601955565b60135461010090046001600160a01b031633146110d15760405162461bcd60e51b8152600401610afb90613d68565b601b55565b336000908152601c602052604090205460ff16806110fe57506017546001600160a01b031633145b80611118575060135461010090046001600160a01b031633145b6111345760405162461bcd60e51b8152600401610afb90613fdc565b6001600160a01b0382166000818152601d6020526040808220805460ff191685151590811790915590519092917f0ba00283699e8a51615f49e891f96d655cb66dee99cdab7fd2dca130c251818291a35050565b6060816001600160401b038111156111a2576111a26131bd565b6040519080825280602002602001820160405280156111e757816020015b60408051808201909152600080825260208201528152602001906001900390816111c05790505b50905060005b828110156112f957600f600085858481811061120b5761120b613e0a565b905060400201600001358152602001908152602001600020600085858481811061123757611237613e0a565b9050604002016020013581526020019081526020016000205482828151811061126257611262613e0a565b6020908102919091010151526010600085858481811061128457611284613e0a565b90506040020160000135815260200190815260200160002060008585848181106112b0576112b0613e0a565b905060400201602001358152602001908152602001600020548282815181106112db576112db613e0a565b60209081029190910181015101526112f281613e36565b90506111ed565b5092915050565b601454600160a01b900460ff161561132a5760405162461bcd60e51b8152600401610afb90613e4f565b60135460ff1661134c5760405162461bcd60e51b8152600401610afb90613e86565b6013805460ff19169055600b54604051634af46b4560e11b8152600481018990528891600091829182916001600160a01b03909116906395e8d68a90602401606060405180830381865afa1580156113a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113cc9190613ece565b9194509250905060ff831660051480156113e65750814210155b80156113f25750804211155b61140e5760405162461bcd60e51b8152600401610afb90613f03565b6000611418612cd3565b905060006040518061014001604052808e81526020018d81526020018c815260200160165481526020018b8152602001601860008f8152602001908152602001600020548152602001611469610b30565b81526020018a6001600160a01b0316815260200189898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b0384166020909101526040516301f4bd6760e61b815290915073ee91925aacca9b96a77a977ab1dd1688f596858e90637d2f59c090611503906005908590600401613fff565b60006040518083038186803b15801561151b57600080fd5b505af415801561152f573d6000803e3d6000fd5b50506013805460ff19166001179055505050505050505050505050505050565b336000908152601c602052604090205460ff168061157757506017546001600160a01b031633145b80611591575060135461010090046001600160a01b031633145b6115ad5760405162461bcd60e51b8152600401610afb90613fdc565b611622600085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f89018190048102820181019092528781529250879150869081908401838280828437600092019190915250612d0692505050565b50505050565b60135461010090046001600160a01b031633146116575760405162461bcd60e51b8152600401610afb90613d68565b6001600160a01b0381166116ad5760405162461bcd60e51b815260206004820152601d60248201527f4e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610afb565b6013546040516001600160a01b0380841692610100900416907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3601380546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b601454600160a01b900460ff161561173e5760405162461bcd60e51b8152600401610afb90613e4f565b6000606061174b8b612d49565b60408051610100810182528f8152602081018f90529081018d9052606081018c9052608081018b90526001600160a01b03808b1660a083015289811660c0830152600c54939550919350600092909160e083019116156117b657600c546001600160a01b03166117c8565b60135461010090046001600160a01b03165b6001600160a01b0390811690915260408051633c394fd560e21b815260056004820152835160248201526020840151604482015290830151606482015260608301516084820152608083015160a482015260a0830151821660c482015260c0830151821660e482015260e083015190911661010482015290915073ee91925aacca9b96a77a977ab1dd1688f596858e9063f0e53f5490610124015b60006040518083038186803b15801561187b57600080fd5b505af415801561188f573d6000803e3d6000fd5b5050505060006119026016548f8e8e8e8e8e6040516020016118ee979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b0390811660a08401521660c082015260e00190565b604051602081830303815290604052610c3e565b90506119488482858a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c9250612e25915050565b5050505050505050505050505050565b600c5460009081906001600160a01b031680611981575060135461010090046001600160a01b03165b6001600160a01b0381166119cd5760405162461bcd60e51b8152602060048201526013602482015272696e76616c696420736d6746656550726f787960681b6044820152606401610afb565b60005b84811015611aba57600e60008787848181106119ee576119ee613e0a565b90506020020135815260200190815260200160002054925060056009016000878784818110611a1f57611a1f613e0a565b90506020020135815260200190815260200160002060009055611a4b8385612f3390919063ffffffff16565b9350816001600160a01b031642878784818110611a6a57611a6a613e0a565b905060200201357ff12b3f379096849c585fc75843457b18f02c980d39f9462c0ccefc992f2cb87b86604051611aa291815260200190565b60405180910390a4611ab381613e36565b90506119d0565b508215610b2957610b298184611ace610b30565b612f46565b336000908152601d602052604090205460ff1680611b005750336000908152601c602052604090205460ff165b80611b1557506017546001600160a01b031633145b80611b2f575060135461010090046001600160a01b031633145b611b4b5760405162461bcd60e51b8152600401610afb90613de4565b80356000818152600f60209081526040808320828601358085529083528184208287013590819055858552601084528285208286528452938290206060870135908190558251948552928401929092529092917f2c40e30353ae48a032fd20f1fece20031c1b80a2bc8512a2c172ff4de2e59519910160405180910390a350565b60135461010090046001600160a01b03163314611bfb5760405162461bcd60e51b8152600401610afb90613d68565b6001600160a01b0382166000818152601c6020526040808220805460ff191685151590811790915590519092917fb0952cae2bb8b955d827c964f844b30447210f1f21be8c009772a3044a76534491a35050565b60135461010090046001600160a01b03163314611c7e5760405162461bcd60e51b8152600401610afb90613d68565b601780546001600160a01b0319166001600160a01b0383169081179091556040519081527f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a19060200160405180910390a150565b60135461010090046001600160a01b03163314611d015760405162461bcd60e51b8152600401610afb90613d68565b60138054610100600160a81b0319169055565b601454600160a01b900460ff1615611d3e5760405162461bcd60e51b8152600401610afb90613e4f565b60006060611d4b8b612d49565b809250819350505060006040518061012001604052808e81526020018d81526020018c81526020018b81526020018a8152602001611d87610b30565b81526001600160a01b03808b1660208301528981166040830152600c546060909201911615611dc157600c546001600160a01b0316611dd3565b60135461010090046001600160a01b03165b6001600160a01b0316905260405163135122a560e21b815290915073ee91925aacca9b96a77a977ab1dd1688f596858e90634d448a94906118639060059085906004016140b0565b6014546001600160a01b03163303611e5757601454601380546001600160a01b0390921661010002610100600160a81b03199092169190911790555b565b6000611ed0600086868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8a01819004810282018101909252888152925088915087908190840183828082843760009201919091525061307092505050565b95945050505050565b6060816001600160401b03811115611ef357611ef36131bd565b604051908082528060200260200182016040528015611f1c578160200160208202803683370190505b50905060005b828110156112f95760186000858584818110611f4057611f40613e0a565b90506020020135815260200190815260200160002054828281518110611f6857611f68613e0a565b6020908102919091010152611f7c81613e36565b9050611f22565b336000908152601c602052604090205460ff1680611fab57506017546001600160a01b031633145b80611fc5575060135461010090046001600160a01b031633145b611fe15760405162461bcd60e51b8152600401610afb90613fdc565b610b29600086868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8a0181900481028201810190925288815292508891508790819084018382808284376000920191909152508792506130b8915050565b601454600160a01b900460ff16156120825760405162461bcd60e51b8152600401610afb90613e4f565b6000606061208f8a612d49565b6040805160e0810182528e8152602081018e90528082018d9052606081018c9052608081018b90526001600160a01b03808b1660a0830152891660c08201529051630bc60e7f60e21b815292945090925090731faa6f2b3c252da20820593d6de5c40ffcaf765590632f1839fc9061210e906005908590600401614140565b60006040518083038186803b15801561212657600080fd5b505af415801561213a573d6000803e3d6000fd5b5050505060006121626016548e8d8d8d8d8d6040516020016118ee97969594939291906141cf565b90506121718482858989612e25565b50505050505050505050505050565b60135461010090046001600160a01b031633146121af5760405162461bcd60e51b8152600401610afb90613d68565b601480546001600160a01b0319166001600160a01b0392909216919091179055565b601454600160a01b900460ff16156121fb5760405162461bcd60e51b8152600401610afb90613e4f565b60135460ff1661221d5760405162461bcd60e51b8152600401610afb90613e86565b6013805460ff19169055600b54604051634af46b4560e11b8152600481018790528691600091829182916001600160a01b03909116906395e8d68a90602401606060405180830381865afa158015612279573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229d9190613ece565b9194509250905060ff831660051480156122b75750814210155b80156122c35750804211155b6122df5760405162461bcd60e51b8152600401610afb90613f03565b600087511180156122f857506122f3612b33565b875111155b6123355760405162461bcd60e51b815260206004820152600e60248201526d092dcecc2d8d2c840d8cadccee8d60931b6044820152606401610afb565b85518751146123785760405162461bcd60e51b815260206004820152600f60248201526e098cadccee8d040dad2e6dac2e8c6d608b1b6044820152606401610afb565b60006040518061012001604052808b81526020018a81526020018981526020018881526020016016548152602001601860008c81526020019081526020016000205481526020016123c7610b30565b81526020018781526020016123da612cd3565b6001600160a01b031690526040516322a0e81760e21b8152909150731faa6f2b3c252da20820593d6de5c40ffcaf765590638a83a05c9061242290600590859060040161422e565b60006040518083038186803b15801561243a57600080fd5b505af415801561244e573d6000803e3d6000fd5b50506013805460ff19166001179055505050505050505050505050565b601454600160a01b900460ff16156124955760405162461bcd60e51b8152600401610afb90613e4f565b600060606124a28b612d49565b60408051610100810182528f8152602081018f90528082018e9052606081018d9052608081018c905260a081018b90526001600160a01b03808b1660c0830152891660e082015290516343966d8360e11b815292945090925090731faa6f2b3c252da20820593d6de5c40ffcaf76559063872cdb06906125299060059085906004016142e5565b60006040518083038186803b15801561254157600080fd5b505af4158015612555573d6000803e3d6000fd5b50505050600061257f6016548f8e8e8e8e8e8e6040516020016118ee98979695949392919061439c565b90506119488482858989612e25565b6000828152601860205260408082205460165491516337e99c6160e21b8152600560048201526024810186905260448101919091526064810191909152608481018390528190731faa6f2b3c252da20820593d6de5c40ffcaf76559063dfa671849060a4016040805180830381865af415801561260f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed09190614413565b336000908152601d602052604090205460ff16806126605750336000908152601c602052604090205460ff165b8061267557506017546001600160a01b031633145b8061268f575060135461010090046001600160a01b031633145b6126ab5760405162461bcd60e51b8152600401610afb90613de4565b60005b81811015610e02578282828181106126c8576126c8613e0a565b905060800201604001356005600a0160008585858181106126eb576126eb613e0a565b905060800201600001358152602001908152602001600020600085858581811061271757612717613e0a565b9050608002016020013581526020019081526020016000208190555082828281811061274557612745613e0a565b905060800201606001356005600b01600085858581811061276857612768613e0a565b905060800201600001358152602001908152602001600020600085858581811061279457612794613e0a565b905060800201602001358152602001908152602001600020819055508282828181106127c2576127c2613e0a565b905060800201602001358383838181106127de576127de613e0a565b905060800201600001357f2c40e30353ae48a032fd20f1fece20031c1b80a2bc8512a2c172ff4de2e5951985858581811061281b5761281b613e0a565b9050608002016040013586868681811061283757612837613e0a565b90506080020160600135604051612858929190918252602082015260400190565b60405180910390a361286981613e36565b90506126ae565b336000908152601c602052604090205460ff168061289857506017546001600160a01b031633145b806128b2575060135461010090046001600160a01b031633145b6128ce5760405162461bcd60e51b8152600401610afb90613fdc565b601a55565b601454600160a01b900460ff16156128fd5760405162461bcd60e51b8152600401610afb90613e4f565b60135460ff1661291f5760405162461bcd60e51b8152600401610afb90613e86565b6013805460ff19169055600b54604051634af46b4560e11b8152600481018890528791600091829182916001600160a01b03909116906395e8d68a90602401606060405180830381865afa15801561297b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061299f9190613ece565b9194509250905060ff831660051480156129b95750814210155b80156129c55750804211155b6129e15760405162461bcd60e51b8152600401610afb90613f03565b600088511180156129fa57506129f5612b33565b885111155b612a375760405162461bcd60e51b815260206004820152600e60248201526d092dcecc2d8d2c840d8cadccee8d60931b6044820152606401610afb565b8651885114612a7a5760405162461bcd60e51b815260206004820152600f60248201526e098cadccee8d040dad2e6dac2e8c6d608b1b6044820152606401610afb565b60006040518061014001604052808c81526020018b81526020018a81526020018981526020016016548152602001601860008d8152602001908152602001600020548152602001612ac9610b30565b8152602001886001600160a01b03168152602001878152602001612aeb612cd3565b6001600160a01b0316905260405163032a0d7760e11b8152909150731faa6f2b3c252da20820593d6de5c40ffcaf7655906306541aee90610ff5906005908590600401614441565b6000601954600003612b455750601490565b5060195490565b336000908152601c602052604090205460ff1680612b7457506017546001600160a01b031633145b80612b8e575060135461010090046001600160a01b031633145b612baa5760405162461bcd60e51b8152600401610afb90613fdc565b601654600003612bba5760168190555b50565b336000908152601d602052604090205460ff1680612bea5750336000908152601c602052604090205460ff165b80612bff57506017546001600160a01b031633145b80612c19575060135461010090046001600160a01b031633145b612c355760405162461bcd60e51b8152600401610afb90613de4565b600082815260186020526040908190208290555182907fdfa3e1a2556a2caf7af0a1cb98a9eed056ae433c4e109e3398edff9863d45bf590612c7a9084815260200190565b60405180910390a25050565b60135461010090046001600160a01b03163314612cb55760405162461bcd60e51b8152600401610afb90613d68565b60148054911515600160a01b0260ff60a01b19909216919091179055565b600c546000906001600160a01b03168015612cee5780612d00565b60135461010090046001600160a01b03165b91505090565b6040518390612d16908490613daf565b908152602001604051809103902081604051612d329190613daf565b908152602001604051809103902060009055505050565b600b546040516344cefb6960e01b8152600481018390526000916060918391829182916001600160a01b0316906344cefb6990602401600060405180830381865afa158015612d9c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612dc4919081019061455b565b949e50919c50969a50985090965060059550612de1945050505050565b60ff168360ff16148015612df55750814210155b8015612e015750804211155b612e1d5760405162461bcd60e51b8152600401610afb90613f03565b505050915091565b6020838101516040808601518584015186830151600d548451631161eded60e21b8152600481018d90526024810189905260448101879052606481018590526084810184905260a4810183905260c481018c9052945195969395929491936001600160a01b0390911692634587b7b49260e480820193929182900301816000875af1158015612eb8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612edc9190614623565b612f285760405162461bcd60e51b815260206004820152601d60248201527f5369676e617475726520766572696669636174696f6e206661696c65640000006044820152606401610afb565b505050505050505050565b6000612f3f8284614640565b9392505050565b81471015612fa25760405162461bcd60e51b815260206004820152602360248201527f45746865725472616e736665723a20696e73756666696369656e742062616c616044820152626e636560e81b6064820152608401610afb565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d8060008114612ff2576040519150601f19603f3d011682016040523d82523d6000602084013e612ff7565b606091505b5050905080611622576040805162461bcd60e51b81526020600482015260248101919091527f45746865725472616e736665723a20756e61626c6520746f2073656e6420766160448201527f6c75652c20726563697069656e74206d617920686176652072657665727465646064820152608401610afb565b600083600001836040516130849190613daf565b9081526020016040518091039020826040516130a09190613daf565b90815260200160405180910390205490509392505050565b8084600001846040516130cb9190613daf565b9081526020016040518091039020836040516130e79190613daf565b9081526040519081900360200190205550505050565b6001600160a01b0381168114612bba57600080fd5b8035610cac816130fd565b600080600080600060a0868803121561313557600080fd5b8535613140816130fd565b94506020860135613150816130fd565b93506040860135613160816130fd565b92506060860135613170816130fd565b91506080860135613180816130fd565b809150509295509295909350565b6000604082840312156131a057600080fd5b50919050565b815181526020808301519082015260408101610ca6565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156131fb576131fb6131bd565b604052919050565b60006001600160401b0382111561321c5761321c6131bd565b50601f01601f191660200190565b600082601f83011261323b57600080fd5b813561324e61324982613203565b6131d3565b81815284602083860101111561326357600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561329657600080fd5b84356132a1816130fd565b935060208501356132b1816130fd565b92506040850135915060608501356001600160401b038111156132d357600080fd5b6132df8782880161322a565b91505092959194509250565b6000602082840312156132fd57600080fd5b81356001600160401b0381111561331357600080fd5b61331f8482850161322a565b949350505050565b60008083601f84011261333957600080fd5b5081356001600160401b0381111561335057600080fd5b6020830191508360208260061b850101111561336b57600080fd5b9250929050565b6000806020838503121561338557600080fd5b82356001600160401b0381111561339b57600080fd5b6133a785828601613327565b90969095509350505050565b6000602082840312156133c557600080fd5b8135612f3f816130fd565b60008083601f8401126133e257600080fd5b5081356001600160401b038111156133f957600080fd5b60208301915083602082850101111561336b57600080fd5b60008060008060006080868803121561342957600080fd5b85359450602086013593506040860135925060608601356001600160401b0381111561345457600080fd5b613460888289016133d0565b969995985093965092949392505050565b60006020828403121561348357600080fd5b5035919050565b8015158114612bba57600080fd5b600080604083850312156134ab57600080fd5b82356134b6816130fd565b915060208301356134c68161348a565b809150509250929050565b602080825282518282018190526000919060409081850190868401855b8281101561351b5761350b84835180518252602090810151910152565b92840192908501906001016134ee565b5091979650505050505050565b600080600080600080600060c0888a03121561354357600080fd5b87359650602088013595506040880135945060608801359350608088013561356a816130fd565b925060a08801356001600160401b0381111561358557600080fd5b6135918a828b016133d0565b989b979a50959850939692959293505050565b600080600080604085870312156135ba57600080fd5b84356001600160401b03808211156135d157600080fd5b6135dd888389016133d0565b909650945060208701359150808211156135f657600080fd5b50613603878288016133d0565b95989497509550505050565b6000806000806000806000806000806101208b8d03121561362f57600080fd5b8a35995060208b0135985060408b0135975060608b0135965060808b0135955060a08b013561365d816130fd565b945060c08b013561366d816130fd565b935060e08b01356001600160401b0381111561368857600080fd5b6136948d828e016133d0565b915080945050809250506101008b013590509295989b9194979a5092959850565b60008083601f8401126136c757600080fd5b5081356001600160401b038111156136de57600080fd5b6020830191508360208260051b850101111561336b57600080fd5b6000806020838503121561370c57600080fd5b82356001600160401b0381111561372257600080fd5b6133a7858286016136b5565b6000608082840312156131a057600080fd5b600081518084526020808501945080840160005b8381101561377057815187529582019590820190600101613754565b509495945050505050565b602081526000612f3f6020830184613740565b6000806000806000606086880312156137a657600080fd5b85356001600160401b03808211156137bd57600080fd5b6137c989838a016133d0565b909750955060208801359150808211156137e257600080fd5b506137ef888289016133d0565b96999598509660400135949350505050565b600082601f83011261381257600080fd5b813560206001600160401b0382111561382d5761382d6131bd565b8160051b61383c8282016131d3565b928352848101820192828101908785111561385657600080fd5b83870192505b848310156138755782358252918301919083019061385c565b979650505050505050565b60008060008060008060008060006101208a8c03121561389f57600080fd5b8935985060208a0135975060408a0135965060608a01356001600160401b03808211156138cb57600080fd5b6138d78d838e01613801565b975060808c01359150808211156138ed57600080fd5b6138f98d838e01613801565b965061390760a08d01613112565b955061391560c08d01613112565b945060e08c013591508082111561392b57600080fd5b506139388c828d0161322a565b9250506101008a013590509295985092959850929598565b600080600080600060a0868803121561396857600080fd5b853594506020860135935060408601356001600160401b038082111561398d57600080fd5b61399989838a01613801565b945060608801359150808211156139af57600080fd5b6139bb89838a01613801565b935060808801359150808211156139d157600080fd5b506139de8882890161322a565b9150509295509295909350565b6000806000806000806000806000806101408b8d031215613a0b57600080fd5b8a35995060208b0135985060408b0135975060608b01356001600160401b0380821115613a3757600080fd5b613a438e838f01613801565b985060808d0135915080821115613a5957600080fd5b613a658e838f01613801565b975060a08d0135915080821115613a7b57600080fd5b613a878e838f0161322a565b9650613a9560c08e01613112565b9550613aa360e08e01613112565b94506101008d0135915080821115613aba57600080fd5b50613ac78d828e0161322a565b9250506101208b013590509295989b9194979a5092959850565b60008060008060008060008060a0898b031215613afd57600080fd5b8835613b08816130fd565b97506020890135613b18816130fd565b965060408901356001600160401b0380821115613b3457600080fd5b613b408c838d016136b5565b909850965060608b0135915080821115613b5957600080fd5b613b658c838d016136b5565b909650945060808b0135915080821115613b7e57600080fd5b50613b8b8b828c016133d0565b999c989b5096995094979396929594505050565b60008060408385031215613bb257600080fd5b50508035926020909101359150565b60008060208385031215613bd457600080fd5b82356001600160401b0380821115613beb57600080fd5b818501915085601f830112613bff57600080fd5b813581811115613c0e57600080fd5b8660208260071b8501011115613c2357600080fd5b60209290920196919550909350505050565b60008060008060008060c08789031215613c4e57600080fd5b863595506020870135945060408701356001600160401b0380821115613c7357600080fd5b613c7f8a838b01613801565b95506060890135915080821115613c9557600080fd5b613ca18a838b01613801565b945060808901359150613cb3826130fd565b90925060a08801359080821115613cc957600080fd5b50613cd689828a0161322a565b9150509295509295509295565b600080600080600060a08688031215613cfb57600080fd5b8535613d06816130fd565b94506020860135613d16816130fd565b9350604086013592506060860135915060808601356001600160401b03811115613d3f57600080fd5b6139de8882890161322a565b600060208284031215613d5d57600080fd5b8135612f3f8161348a565b6020808252600990820152682737ba1037bbb732b960b91b604082015260600190565b60005b83811015613da6578181015183820152602001613d8e565b50506000910152565b60008251613dc1818460208701613d8b565b9190910192915050565b600060208284031215613ddd57600080fd5b5051919050565b6020808252600c908201526b3737ba1037b832b930ba37b960a11b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201613e4857613e48613e20565b5060010190565b60208082526018908201527f536d61727420636f6e74726163742069732068616c7465640000000000000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b805160ff81168114610cac57600080fd5b600080600060608486031215613ee357600080fd5b613eec84613ebd565b925060208401519150604084015190509250925092565b6020808252600f908201526e504b206973206e6f7420726561647960881b604082015260600190565b60008151808452613f44816020860160208601613d8b565b601f01601f19169290920160200192915050565b82815260406020820152815160408201526020820151606082015260408201516080820152606082015160a0820152608082015160c082015260a082015160e0820152600060c0830151610100808185015250613fb9610140840182613f2c565b60e094909401516001600160a01b03166101209390930192909252509092915050565b6020808252600990820152683737ba1030b236b4b760b91b604082015260600190565b82815260406020820152815160408201526020820151606082015260408201516080820152606082015160a0820152608082015160c082015260a082015160e0820152600060c0830151610100818185015260e0850151915061012061406f818601846001600160a01b03169052565b8186015192506101409150818286015261408d610180860184613f2c565b908601516001600160a01b03811661016087015290925090505b50949350505050565b60006101408201905083825282516020830152602083015160408301526040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160018060a01b0380821660e085015260e085015191506101008183168186015280860151925050506141386101208401826001600160a01b03169052565b509392505050565b828152604060208201528151604082015260208201516060820152604082015160808201526000606083015160e060a0840152614181610120840182613740565b90506080840151603f198483030160c085015261419e8282613740565b60a08601516001600160a01b0390811660e087015260c0909601519095166101009094019390935250919392505050565b87815286602082015285604082015260e0606082015260006141f460e0830187613740565b82810360808401526142068187613740565b6001600160a01b0395861660a08501529390941660c090920191909152509695505050505050565b82815260406020820152815160408201526020820151606082015260006040830151610120806080850152614267610160850183613740565b91506060850151603f19808685030160a08701526142858483613740565b9350608087015160c087015260a087015160e087015260c08701519150610100828188015260e088015192508187860301848801526142c48584613f2c565b94508088015193505050506140a76101408501826001600160a01b03169052565b82815260406020820152815160408201526020820151606082015260408201516080820152600060608301516101008060a0850152614328610140850183613740565b91506080850151603f19808685030160c08701526143468483613740565b935060a08701519150808685030160e0870152506143648382613f2c565b92505060c0850151614380828601826001600160a01b03169052565b505060e08401516001600160a01b0381166101208501526140a7565b60006101008a83528960208401528860408401528060608401526143c281840189613740565b905082810360808401526143d68188613740565b905082810360a08401526143ea8187613f2c565b6001600160a01b0395861660c08501529390941660e09092019190915250979650505050505050565b6000806040838503121561442657600080fd5b8251614431816130fd565b6020939093015192949293505050565b8281526040602082015281516040820152602082015160608201526000604083015161014080608085015261447a610180850183613740565b91506060850151603f19808685030160a08701526144988483613740565b9350608087015160c087015260a087015160e087015260c08701519150610100828188015260e088015192506101206144db818901856001600160a01b03169052565b8189015193508288870301858901526144f48685613f2c565b9550808901519450505050506140a76101608501826001600160a01b03169052565b600082601f83011261452757600080fd5b815161453561324982613203565b81815284602083860101111561454a57600080fd5b61331f826020830160208701613d8b565b60008060008060008060008060008060006101608c8e03121561457d57600080fd5b8b519a5061458d60208d01613ebd565b995060408c0151985060608c0151975060808c0151965060a08c0151955060c08c0151945060e08c01516001600160401b038111156145cb57600080fd5b6145d78e828f01614516565b9450506101008c01516001600160401b038111156145f457600080fd5b6146008e828f01614516565b9350506101208c015191506101408c015190509295989b509295989b9093969950565b60006020828403121561463557600080fd5b8151612f3f8161348a565b80820180821115610ca657610ca6613e2056fea26469706673582212202292ee3d7aa19787413dc8c7498fa124ab91bfb1eee0e945cf36bf3ff4ac9d7164736f6c63430008120033

Deployed Bytecode

0x60806040526004361061031e5760003560e01c806373e29b0d116101ab578063bbf485ab116100f7578063e7c00f6e11610095578063f0baef521161006f578063f0baef5214610a1a578063f23a6e6114610a3a578063f495438714610a66578063f851a44014610a865761032d565b8063e7c00f6e146109cf578063e92e2c1b146109e4578063ed8d47e6146109fa5761032d565b8063d4ee1d90116100d1578063d4ee1d901461095c578063d52012bc1461097c578063dd27b6cd1461099c578063e02dee8c146109bc5761032d565b8063bbf485ab146108ed578063bc197c811461090d578063be5212a81461093c5761032d565b8063926731ea11610164578063a8b382051161013e578063a8b382051461088d578063b179e1e7146108a3578063b64ed6db146108b9578063b9b8af0b146108cc5761032d565b8063926731ea1461082d578063a3d1e3811461084d578063a6f9dae11461086d5761032d565b806373e29b0d1461072f57806373fa58f51461078657806379ba5097146107a65780638061babf146107bb5780638da5cb5b146107db57806390116916146108005761032d565b8063392a62b91161026a57806354c0de4a116102235780636d70f7ae116101fd5780636d70f7ae1461069d578063704b6c02146106cd578063715018a6146106ed578063715f250c146107025761032d565b806354c0de4a1461062b5780635c60da1b1461064b5780635dd939ba1461067d5761032d565b8063392a62b91461056b57806341ff1bcc146105985780634cc7125b146105ab5780634fb2e45d146105cb57806350cc6707146105eb578063536686a91461060b5761032d565b8063213a2596116102d75780632b26a6bf116102b15780632b26a6bf146104de5780632e96be19146104fe57806333b57a401461051e578063361b31bf1461053e5761032d565b8063213a25961461046b57806324d7806c1461048b578063257011b6146104cb5761032d565b806301f4d28d146103355780630a72b1ab1461035d5780630d3adbac1461037d5780630ec61b7914610393578063150b7a0214610407578063159982f01461044b5761032d565b3661032d5761032b610aa6565b005b61032b610aa6565b34801561034157600080fd5b5061034a610b30565b6040519081526020015b60405180910390f35b34801561036957600080fd5b5061032b61037836600461311d565b610b4a565b34801561038957600080fd5b5061034a601b5481565b34801561039f57600080fd5b506103fa6103ae36600461318e565b604080518082018252600080825260208083018281528535808452600f835285842096830135808552968352858420548552835260108252848320958352949052919091205490915290565b60405161035491906131a6565b34801561041357600080fd5b50610432610422366004613280565b630a85bd0160e11b949350505050565b6040516001600160e01b03199091168152602001610354565b34801561045757600080fd5b5061034a6104663660046132eb565b610c3e565b34801561047757600080fd5b5061032b610486366004613372565b610cb1565b34801561049757600080fd5b506104bb6104a63660046133b3565b601c6020526000908152604090205460ff1681565b6040519015158152602001610354565b61032b6104d9366004613411565b610e07565b3480156104ea57600080fd5b5061032b6104f9366004613471565b61103f565b34801561050a57600080fd5b5061032b610519366004613471565b6110a2565b34801561052a57600080fd5b5061032b610539366004613498565b6110d6565b34801561054a57600080fd5b5061055e610559366004613372565b611188565b60405161035491906134d1565b34801561057757600080fd5b5061034a610586366004613471565b60009081526018602052604090205490565b61032b6105a6366004613528565b611300565b3480156105b757600080fd5b5061032b6105c63660046135a4565b61154f565b3480156105d757600080fd5b5061032b6105e63660046133b3565b611628565b3480156105f757600080fd5b5061032b61060636600461360f565b611714565b34801561061757600080fd5b5061032b6106263660046136f9565b611958565b34801561063757600080fd5b5061032b61064636600461372e565b611ad3565b34801561065757600080fd5b506015546001600160a01b03165b6040516001600160a01b039091168152602001610354565b34801561068957600080fd5b5061032b610698366004613498565b611bcc565b3480156106a957600080fd5b506104bb6106b83660046133b3565b601d6020526000908152604090205460ff1681565b3480156106d957600080fd5b5061032b6106e83660046133b3565b611c4f565b3480156106f957600080fd5b5061032b611cd2565b34801561070e57600080fd5b5061034a61071d366004613471565b6000908152600e602052604090205490565b34801561073b57600080fd5b50600a54600b54600c54600954600d54604080516001600160a01b0396871681529486166020860152928516928401929092528316606083015291909116608082015260a001610354565b34801561079257600080fd5b5061032b6107a136600461360f565b611d14565b3480156107b257600080fd5b5061032b611e1b565b3480156107c757600080fd5b5061034a6107d63660046135a4565b611e59565b3480156107e757600080fd5b506013546106659061010090046001600160a01b031681565b34801561080c57600080fd5b5061082061081b3660046136f9565b611ed9565b604051610354919061377b565b34801561083957600080fd5b5061032b61084836600461378e565b611f83565b34801561085957600080fd5b5061032b610868366004613880565b612058565b34801561087957600080fd5b5061032b6108883660046133b3565b612180565b34801561089957600080fd5b5061034a60115481565b3480156108af57600080fd5b5061034a60165481565b61032b6108c7366004613950565b6121d1565b3480156108d857600080fd5b506014546104bb90600160a01b900460ff1681565b3480156108f957600080fd5b5061032b6109083660046139eb565b61246b565b34801561091957600080fd5b50610432610928366004613ae1565b63bc197c8160e01b98975050505050505050565b34801561094857600080fd5b5061034a610957366004613b9f565b61258e565b34801561096857600080fd5b50601454610665906001600160a01b031681565b34801561098857600080fd5b5061032b610997366004613bc1565b612633565b3480156109a857600080fd5b5061032b6109b7366004613471565b612870565b61032b6109ca366004613c35565b6128d3565b3480156109db57600080fd5b5061034a612b33565b3480156109f057600080fd5b5061034a60125481565b348015610a0657600080fd5b5061032b610a15366004613471565b612b4c565b348015610a2657600080fd5b5061032b610a35366004613b9f565b612bbd565b348015610a4657600080fd5b50610432610a55366004613ce3565b63f23a6e6160e01b95945050505050565b348015610a7257600080fd5b5061032b610a81366004613d4b565b612c86565b348015610a9257600080fd5b50601754610665906001600160a01b031681565b6015546001600160a01b031680610b045760405162461bcd60e51b815260206004820152601f60248201527f696d706c656d656e746174696f6e20636f6e7472616374206e6f74207365740060448201526064015b60405180910390fd5b60405136600082376000803683855af43d806000843e818015610b25578184f35b8184fd5b5050505050565b6000601a54600003610b4357506108fc90565b50601a5490565b60135461010090046001600160a01b03163314610b795760405162461bcd60e51b8152600401610afb90613d68565b6001600160a01b03851615801590610b9957506001600160a01b03841615155b8015610bad57506001600160a01b03811615155b610bf05760405162461bcd60e51b815260206004820152601460248201527314185c985b595d195c881a5cc81a5b9d985b1a5960621b6044820152606401610afb565b600b80546001600160a01b03199081166001600160a01b0396871617909155600a8054821696861696909617909555600c805486169385169390931790925550600d80549093169116179055565b6000601b54600103610c565750805160209091012090565b600282604051610c669190613daf565b602060405180830381855afa158015610c83573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610ca69190613dcb565b92915050565b919050565b336000908152601d602052604090205460ff1680610cde5750336000908152601c602052604090205460ff165b80610cf357506017546001600160a01b031633145b80610d0d575060135461010090046001600160a01b031633145b610d295760405162461bcd60e51b8152600401610afb90613de4565b60005b81811015610e0257828282818110610d4657610d46613e0a565b9050604002016020013560186000858585818110610d6657610d66613e0a565b90506040020160000135815260200190815260200160002081905550828282818110610d9457610d94613e0a565b905060400201600001357fdfa3e1a2556a2caf7af0a1cb98a9eed056ae433c4e109e3398edff9863d45bf5848484818110610dd157610dd1613e0a565b90506040020160200135604051610dea91815260200190565b60405180910390a2610dfb81613e36565b9050610d2c565b505050565b601454600160a01b900460ff1615610e315760405162461bcd60e51b8152600401610afb90613e4f565b60135460ff16610e535760405162461bcd60e51b8152600401610afb90613e86565b6013805460ff19169055600b54604051634af46b4560e11b8152600481018790528691600091829182916001600160a01b03909116906395e8d68a90602401606060405180830381865afa158015610eaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed39190613ece565b9194509250905060ff83166005148015610eed5750814210155b8015610ef95750804211155b610f155760405162461bcd60e51b8152600401610afb90613f03565b6000610f1f612cd3565b905060006040518061010001604052808c81526020018b81526020018a81526020016016548152602001601860008d8152602001908152602001600020548152602001610f6a610b30565b815260200189898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b038416602090910152604051638fd59dc760e01b815290915073ee91925aacca9b96a77a977ab1dd1688f596858e90638fd59dc790610ff5906005908590600401613f58565b60006040518083038186803b15801561100d57600080fd5b505af4158015611021573d6000803e3d6000fd5b50506013805460ff1916600117905550505050505050505050505050565b336000908152601c602052604090205460ff168061106757506017546001600160a01b031633145b80611081575060135461010090046001600160a01b031633145b61109d5760405162461bcd60e51b8152600401610afb90613fdc565b601955565b60135461010090046001600160a01b031633146110d15760405162461bcd60e51b8152600401610afb90613d68565b601b55565b336000908152601c602052604090205460ff16806110fe57506017546001600160a01b031633145b80611118575060135461010090046001600160a01b031633145b6111345760405162461bcd60e51b8152600401610afb90613fdc565b6001600160a01b0382166000818152601d6020526040808220805460ff191685151590811790915590519092917f0ba00283699e8a51615f49e891f96d655cb66dee99cdab7fd2dca130c251818291a35050565b6060816001600160401b038111156111a2576111a26131bd565b6040519080825280602002602001820160405280156111e757816020015b60408051808201909152600080825260208201528152602001906001900390816111c05790505b50905060005b828110156112f957600f600085858481811061120b5761120b613e0a565b905060400201600001358152602001908152602001600020600085858481811061123757611237613e0a565b9050604002016020013581526020019081526020016000205482828151811061126257611262613e0a565b6020908102919091010151526010600085858481811061128457611284613e0a565b90506040020160000135815260200190815260200160002060008585848181106112b0576112b0613e0a565b905060400201602001358152602001908152602001600020548282815181106112db576112db613e0a565b60209081029190910181015101526112f281613e36565b90506111ed565b5092915050565b601454600160a01b900460ff161561132a5760405162461bcd60e51b8152600401610afb90613e4f565b60135460ff1661134c5760405162461bcd60e51b8152600401610afb90613e86565b6013805460ff19169055600b54604051634af46b4560e11b8152600481018990528891600091829182916001600160a01b03909116906395e8d68a90602401606060405180830381865afa1580156113a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113cc9190613ece565b9194509250905060ff831660051480156113e65750814210155b80156113f25750804211155b61140e5760405162461bcd60e51b8152600401610afb90613f03565b6000611418612cd3565b905060006040518061014001604052808e81526020018d81526020018c815260200160165481526020018b8152602001601860008f8152602001908152602001600020548152602001611469610b30565b81526020018a6001600160a01b0316815260200189898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b0384166020909101526040516301f4bd6760e61b815290915073ee91925aacca9b96a77a977ab1dd1688f596858e90637d2f59c090611503906005908590600401613fff565b60006040518083038186803b15801561151b57600080fd5b505af415801561152f573d6000803e3d6000fd5b50506013805460ff19166001179055505050505050505050505050505050565b336000908152601c602052604090205460ff168061157757506017546001600160a01b031633145b80611591575060135461010090046001600160a01b031633145b6115ad5760405162461bcd60e51b8152600401610afb90613fdc565b611622600085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f89018190048102820181019092528781529250879150869081908401838280828437600092019190915250612d0692505050565b50505050565b60135461010090046001600160a01b031633146116575760405162461bcd60e51b8152600401610afb90613d68565b6001600160a01b0381166116ad5760405162461bcd60e51b815260206004820152601d60248201527f4e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610afb565b6013546040516001600160a01b0380841692610100900416907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3601380546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b601454600160a01b900460ff161561173e5760405162461bcd60e51b8152600401610afb90613e4f565b6000606061174b8b612d49565b60408051610100810182528f8152602081018f90529081018d9052606081018c9052608081018b90526001600160a01b03808b1660a083015289811660c0830152600c54939550919350600092909160e083019116156117b657600c546001600160a01b03166117c8565b60135461010090046001600160a01b03165b6001600160a01b0390811690915260408051633c394fd560e21b815260056004820152835160248201526020840151604482015290830151606482015260608301516084820152608083015160a482015260a0830151821660c482015260c0830151821660e482015260e083015190911661010482015290915073ee91925aacca9b96a77a977ab1dd1688f596858e9063f0e53f5490610124015b60006040518083038186803b15801561187b57600080fd5b505af415801561188f573d6000803e3d6000fd5b5050505060006119026016548f8e8e8e8e8e6040516020016118ee979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b0390811660a08401521660c082015260e00190565b604051602081830303815290604052610c3e565b90506119488482858a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c9250612e25915050565b5050505050505050505050505050565b600c5460009081906001600160a01b031680611981575060135461010090046001600160a01b03165b6001600160a01b0381166119cd5760405162461bcd60e51b8152602060048201526013602482015272696e76616c696420736d6746656550726f787960681b6044820152606401610afb565b60005b84811015611aba57600e60008787848181106119ee576119ee613e0a565b90506020020135815260200190815260200160002054925060056009016000878784818110611a1f57611a1f613e0a565b90506020020135815260200190815260200160002060009055611a4b8385612f3390919063ffffffff16565b9350816001600160a01b031642878784818110611a6a57611a6a613e0a565b905060200201357ff12b3f379096849c585fc75843457b18f02c980d39f9462c0ccefc992f2cb87b86604051611aa291815260200190565b60405180910390a4611ab381613e36565b90506119d0565b508215610b2957610b298184611ace610b30565b612f46565b336000908152601d602052604090205460ff1680611b005750336000908152601c602052604090205460ff165b80611b1557506017546001600160a01b031633145b80611b2f575060135461010090046001600160a01b031633145b611b4b5760405162461bcd60e51b8152600401610afb90613de4565b80356000818152600f60209081526040808320828601358085529083528184208287013590819055858552601084528285208286528452938290206060870135908190558251948552928401929092529092917f2c40e30353ae48a032fd20f1fece20031c1b80a2bc8512a2c172ff4de2e59519910160405180910390a350565b60135461010090046001600160a01b03163314611bfb5760405162461bcd60e51b8152600401610afb90613d68565b6001600160a01b0382166000818152601c6020526040808220805460ff191685151590811790915590519092917fb0952cae2bb8b955d827c964f844b30447210f1f21be8c009772a3044a76534491a35050565b60135461010090046001600160a01b03163314611c7e5760405162461bcd60e51b8152600401610afb90613d68565b601780546001600160a01b0319166001600160a01b0383169081179091556040519081527f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a19060200160405180910390a150565b60135461010090046001600160a01b03163314611d015760405162461bcd60e51b8152600401610afb90613d68565b60138054610100600160a81b0319169055565b601454600160a01b900460ff1615611d3e5760405162461bcd60e51b8152600401610afb90613e4f565b60006060611d4b8b612d49565b809250819350505060006040518061012001604052808e81526020018d81526020018c81526020018b81526020018a8152602001611d87610b30565b81526001600160a01b03808b1660208301528981166040830152600c546060909201911615611dc157600c546001600160a01b0316611dd3565b60135461010090046001600160a01b03165b6001600160a01b0316905260405163135122a560e21b815290915073ee91925aacca9b96a77a977ab1dd1688f596858e90634d448a94906118639060059085906004016140b0565b6014546001600160a01b03163303611e5757601454601380546001600160a01b0390921661010002610100600160a81b03199092169190911790555b565b6000611ed0600086868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8a01819004810282018101909252888152925088915087908190840183828082843760009201919091525061307092505050565b95945050505050565b6060816001600160401b03811115611ef357611ef36131bd565b604051908082528060200260200182016040528015611f1c578160200160208202803683370190505b50905060005b828110156112f95760186000858584818110611f4057611f40613e0a565b90506020020135815260200190815260200160002054828281518110611f6857611f68613e0a565b6020908102919091010152611f7c81613e36565b9050611f22565b336000908152601c602052604090205460ff1680611fab57506017546001600160a01b031633145b80611fc5575060135461010090046001600160a01b031633145b611fe15760405162461bcd60e51b8152600401610afb90613fdc565b610b29600086868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8a0181900481028201810190925288815292508891508790819084018382808284376000920191909152508792506130b8915050565b601454600160a01b900460ff16156120825760405162461bcd60e51b8152600401610afb90613e4f565b6000606061208f8a612d49565b6040805160e0810182528e8152602081018e90528082018d9052606081018c9052608081018b90526001600160a01b03808b1660a0830152891660c08201529051630bc60e7f60e21b815292945090925090731faa6f2b3c252da20820593d6de5c40ffcaf765590632f1839fc9061210e906005908590600401614140565b60006040518083038186803b15801561212657600080fd5b505af415801561213a573d6000803e3d6000fd5b5050505060006121626016548e8d8d8d8d8d6040516020016118ee97969594939291906141cf565b90506121718482858989612e25565b50505050505050505050505050565b60135461010090046001600160a01b031633146121af5760405162461bcd60e51b8152600401610afb90613d68565b601480546001600160a01b0319166001600160a01b0392909216919091179055565b601454600160a01b900460ff16156121fb5760405162461bcd60e51b8152600401610afb90613e4f565b60135460ff1661221d5760405162461bcd60e51b8152600401610afb90613e86565b6013805460ff19169055600b54604051634af46b4560e11b8152600481018790528691600091829182916001600160a01b03909116906395e8d68a90602401606060405180830381865afa158015612279573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229d9190613ece565b9194509250905060ff831660051480156122b75750814210155b80156122c35750804211155b6122df5760405162461bcd60e51b8152600401610afb90613f03565b600087511180156122f857506122f3612b33565b875111155b6123355760405162461bcd60e51b815260206004820152600e60248201526d092dcecc2d8d2c840d8cadccee8d60931b6044820152606401610afb565b85518751146123785760405162461bcd60e51b815260206004820152600f60248201526e098cadccee8d040dad2e6dac2e8c6d608b1b6044820152606401610afb565b60006040518061012001604052808b81526020018a81526020018981526020018881526020016016548152602001601860008c81526020019081526020016000205481526020016123c7610b30565b81526020018781526020016123da612cd3565b6001600160a01b031690526040516322a0e81760e21b8152909150731faa6f2b3c252da20820593d6de5c40ffcaf765590638a83a05c9061242290600590859060040161422e565b60006040518083038186803b15801561243a57600080fd5b505af415801561244e573d6000803e3d6000fd5b50506013805460ff19166001179055505050505050505050505050565b601454600160a01b900460ff16156124955760405162461bcd60e51b8152600401610afb90613e4f565b600060606124a28b612d49565b60408051610100810182528f8152602081018f90528082018e9052606081018d9052608081018c905260a081018b90526001600160a01b03808b1660c0830152891660e082015290516343966d8360e11b815292945090925090731faa6f2b3c252da20820593d6de5c40ffcaf76559063872cdb06906125299060059085906004016142e5565b60006040518083038186803b15801561254157600080fd5b505af4158015612555573d6000803e3d6000fd5b50505050600061257f6016548f8e8e8e8e8e8e6040516020016118ee98979695949392919061439c565b90506119488482858989612e25565b6000828152601860205260408082205460165491516337e99c6160e21b8152600560048201526024810186905260448101919091526064810191909152608481018390528190731faa6f2b3c252da20820593d6de5c40ffcaf76559063dfa671849060a4016040805180830381865af415801561260f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed09190614413565b336000908152601d602052604090205460ff16806126605750336000908152601c602052604090205460ff165b8061267557506017546001600160a01b031633145b8061268f575060135461010090046001600160a01b031633145b6126ab5760405162461bcd60e51b8152600401610afb90613de4565b60005b81811015610e02578282828181106126c8576126c8613e0a565b905060800201604001356005600a0160008585858181106126eb576126eb613e0a565b905060800201600001358152602001908152602001600020600085858581811061271757612717613e0a565b9050608002016020013581526020019081526020016000208190555082828281811061274557612745613e0a565b905060800201606001356005600b01600085858581811061276857612768613e0a565b905060800201600001358152602001908152602001600020600085858581811061279457612794613e0a565b905060800201602001358152602001908152602001600020819055508282828181106127c2576127c2613e0a565b905060800201602001358383838181106127de576127de613e0a565b905060800201600001357f2c40e30353ae48a032fd20f1fece20031c1b80a2bc8512a2c172ff4de2e5951985858581811061281b5761281b613e0a565b9050608002016040013586868681811061283757612837613e0a565b90506080020160600135604051612858929190918252602082015260400190565b60405180910390a361286981613e36565b90506126ae565b336000908152601c602052604090205460ff168061289857506017546001600160a01b031633145b806128b2575060135461010090046001600160a01b031633145b6128ce5760405162461bcd60e51b8152600401610afb90613fdc565b601a55565b601454600160a01b900460ff16156128fd5760405162461bcd60e51b8152600401610afb90613e4f565b60135460ff1661291f5760405162461bcd60e51b8152600401610afb90613e86565b6013805460ff19169055600b54604051634af46b4560e11b8152600481018890528791600091829182916001600160a01b03909116906395e8d68a90602401606060405180830381865afa15801561297b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061299f9190613ece565b9194509250905060ff831660051480156129b95750814210155b80156129c55750804211155b6129e15760405162461bcd60e51b8152600401610afb90613f03565b600088511180156129fa57506129f5612b33565b885111155b612a375760405162461bcd60e51b815260206004820152600e60248201526d092dcecc2d8d2c840d8cadccee8d60931b6044820152606401610afb565b8651885114612a7a5760405162461bcd60e51b815260206004820152600f60248201526e098cadccee8d040dad2e6dac2e8c6d608b1b6044820152606401610afb565b60006040518061014001604052808c81526020018b81526020018a81526020018981526020016016548152602001601860008d8152602001908152602001600020548152602001612ac9610b30565b8152602001886001600160a01b03168152602001878152602001612aeb612cd3565b6001600160a01b0316905260405163032a0d7760e11b8152909150731faa6f2b3c252da20820593d6de5c40ffcaf7655906306541aee90610ff5906005908590600401614441565b6000601954600003612b455750601490565b5060195490565b336000908152601c602052604090205460ff1680612b7457506017546001600160a01b031633145b80612b8e575060135461010090046001600160a01b031633145b612baa5760405162461bcd60e51b8152600401610afb90613fdc565b601654600003612bba5760168190555b50565b336000908152601d602052604090205460ff1680612bea5750336000908152601c602052604090205460ff165b80612bff57506017546001600160a01b031633145b80612c19575060135461010090046001600160a01b031633145b612c355760405162461bcd60e51b8152600401610afb90613de4565b600082815260186020526040908190208290555182907fdfa3e1a2556a2caf7af0a1cb98a9eed056ae433c4e109e3398edff9863d45bf590612c7a9084815260200190565b60405180910390a25050565b60135461010090046001600160a01b03163314612cb55760405162461bcd60e51b8152600401610afb90613d68565b60148054911515600160a01b0260ff60a01b19909216919091179055565b600c546000906001600160a01b03168015612cee5780612d00565b60135461010090046001600160a01b03165b91505090565b6040518390612d16908490613daf565b908152602001604051809103902081604051612d329190613daf565b908152602001604051809103902060009055505050565b600b546040516344cefb6960e01b8152600481018390526000916060918391829182916001600160a01b0316906344cefb6990602401600060405180830381865afa158015612d9c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612dc4919081019061455b565b949e50919c50969a50985090965060059550612de1945050505050565b60ff168360ff16148015612df55750814210155b8015612e015750804211155b612e1d5760405162461bcd60e51b8152600401610afb90613f03565b505050915091565b6020838101516040808601518584015186830151600d548451631161eded60e21b8152600481018d90526024810189905260448101879052606481018590526084810184905260a4810183905260c481018c9052945195969395929491936001600160a01b0390911692634587b7b49260e480820193929182900301816000875af1158015612eb8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612edc9190614623565b612f285760405162461bcd60e51b815260206004820152601d60248201527f5369676e617475726520766572696669636174696f6e206661696c65640000006044820152606401610afb565b505050505050505050565b6000612f3f8284614640565b9392505050565b81471015612fa25760405162461bcd60e51b815260206004820152602360248201527f45746865725472616e736665723a20696e73756666696369656e742062616c616044820152626e636560e81b6064820152608401610afb565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d8060008114612ff2576040519150601f19603f3d011682016040523d82523d6000602084013e612ff7565b606091505b5050905080611622576040805162461bcd60e51b81526020600482015260248101919091527f45746865725472616e736665723a20756e61626c6520746f2073656e6420766160448201527f6c75652c20726563697069656e74206d617920686176652072657665727465646064820152608401610afb565b600083600001836040516130849190613daf565b9081526020016040518091039020826040516130a09190613daf565b90815260200160405180910390205490509392505050565b8084600001846040516130cb9190613daf565b9081526020016040518091039020836040516130e79190613daf565b9081526040519081900360200190205550505050565b6001600160a01b0381168114612bba57600080fd5b8035610cac816130fd565b600080600080600060a0868803121561313557600080fd5b8535613140816130fd565b94506020860135613150816130fd565b93506040860135613160816130fd565b92506060860135613170816130fd565b91506080860135613180816130fd565b809150509295509295909350565b6000604082840312156131a057600080fd5b50919050565b815181526020808301519082015260408101610ca6565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156131fb576131fb6131bd565b604052919050565b60006001600160401b0382111561321c5761321c6131bd565b50601f01601f191660200190565b600082601f83011261323b57600080fd5b813561324e61324982613203565b6131d3565b81815284602083860101111561326357600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561329657600080fd5b84356132a1816130fd565b935060208501356132b1816130fd565b92506040850135915060608501356001600160401b038111156132d357600080fd5b6132df8782880161322a565b91505092959194509250565b6000602082840312156132fd57600080fd5b81356001600160401b0381111561331357600080fd5b61331f8482850161322a565b949350505050565b60008083601f84011261333957600080fd5b5081356001600160401b0381111561335057600080fd5b6020830191508360208260061b850101111561336b57600080fd5b9250929050565b6000806020838503121561338557600080fd5b82356001600160401b0381111561339b57600080fd5b6133a785828601613327565b90969095509350505050565b6000602082840312156133c557600080fd5b8135612f3f816130fd565b60008083601f8401126133e257600080fd5b5081356001600160401b038111156133f957600080fd5b60208301915083602082850101111561336b57600080fd5b60008060008060006080868803121561342957600080fd5b85359450602086013593506040860135925060608601356001600160401b0381111561345457600080fd5b613460888289016133d0565b969995985093965092949392505050565b60006020828403121561348357600080fd5b5035919050565b8015158114612bba57600080fd5b600080604083850312156134ab57600080fd5b82356134b6816130fd565b915060208301356134c68161348a565b809150509250929050565b602080825282518282018190526000919060409081850190868401855b8281101561351b5761350b84835180518252602090810151910152565b92840192908501906001016134ee565b5091979650505050505050565b600080600080600080600060c0888a03121561354357600080fd5b87359650602088013595506040880135945060608801359350608088013561356a816130fd565b925060a08801356001600160401b0381111561358557600080fd5b6135918a828b016133d0565b989b979a50959850939692959293505050565b600080600080604085870312156135ba57600080fd5b84356001600160401b03808211156135d157600080fd5b6135dd888389016133d0565b909650945060208701359150808211156135f657600080fd5b50613603878288016133d0565b95989497509550505050565b6000806000806000806000806000806101208b8d03121561362f57600080fd5b8a35995060208b0135985060408b0135975060608b0135965060808b0135955060a08b013561365d816130fd565b945060c08b013561366d816130fd565b935060e08b01356001600160401b0381111561368857600080fd5b6136948d828e016133d0565b915080945050809250506101008b013590509295989b9194979a5092959850565b60008083601f8401126136c757600080fd5b5081356001600160401b038111156136de57600080fd5b6020830191508360208260051b850101111561336b57600080fd5b6000806020838503121561370c57600080fd5b82356001600160401b0381111561372257600080fd5b6133a7858286016136b5565b6000608082840312156131a057600080fd5b600081518084526020808501945080840160005b8381101561377057815187529582019590820190600101613754565b509495945050505050565b602081526000612f3f6020830184613740565b6000806000806000606086880312156137a657600080fd5b85356001600160401b03808211156137bd57600080fd5b6137c989838a016133d0565b909750955060208801359150808211156137e257600080fd5b506137ef888289016133d0565b96999598509660400135949350505050565b600082601f83011261381257600080fd5b813560206001600160401b0382111561382d5761382d6131bd565b8160051b61383c8282016131d3565b928352848101820192828101908785111561385657600080fd5b83870192505b848310156138755782358252918301919083019061385c565b979650505050505050565b60008060008060008060008060006101208a8c03121561389f57600080fd5b8935985060208a0135975060408a0135965060608a01356001600160401b03808211156138cb57600080fd5b6138d78d838e01613801565b975060808c01359150808211156138ed57600080fd5b6138f98d838e01613801565b965061390760a08d01613112565b955061391560c08d01613112565b945060e08c013591508082111561392b57600080fd5b506139388c828d0161322a565b9250506101008a013590509295985092959850929598565b600080600080600060a0868803121561396857600080fd5b853594506020860135935060408601356001600160401b038082111561398d57600080fd5b61399989838a01613801565b945060608801359150808211156139af57600080fd5b6139bb89838a01613801565b935060808801359150808211156139d157600080fd5b506139de8882890161322a565b9150509295509295909350565b6000806000806000806000806000806101408b8d031215613a0b57600080fd5b8a35995060208b0135985060408b0135975060608b01356001600160401b0380821115613a3757600080fd5b613a438e838f01613801565b985060808d0135915080821115613a5957600080fd5b613a658e838f01613801565b975060a08d0135915080821115613a7b57600080fd5b613a878e838f0161322a565b9650613a9560c08e01613112565b9550613aa360e08e01613112565b94506101008d0135915080821115613aba57600080fd5b50613ac78d828e0161322a565b9250506101208b013590509295989b9194979a5092959850565b60008060008060008060008060a0898b031215613afd57600080fd5b8835613b08816130fd565b97506020890135613b18816130fd565b965060408901356001600160401b0380821115613b3457600080fd5b613b408c838d016136b5565b909850965060608b0135915080821115613b5957600080fd5b613b658c838d016136b5565b909650945060808b0135915080821115613b7e57600080fd5b50613b8b8b828c016133d0565b999c989b5096995094979396929594505050565b60008060408385031215613bb257600080fd5b50508035926020909101359150565b60008060208385031215613bd457600080fd5b82356001600160401b0380821115613beb57600080fd5b818501915085601f830112613bff57600080fd5b813581811115613c0e57600080fd5b8660208260071b8501011115613c2357600080fd5b60209290920196919550909350505050565b60008060008060008060c08789031215613c4e57600080fd5b863595506020870135945060408701356001600160401b0380821115613c7357600080fd5b613c7f8a838b01613801565b95506060890135915080821115613c9557600080fd5b613ca18a838b01613801565b945060808901359150613cb3826130fd565b90925060a08801359080821115613cc957600080fd5b50613cd689828a0161322a565b9150509295509295509295565b600080600080600060a08688031215613cfb57600080fd5b8535613d06816130fd565b94506020860135613d16816130fd565b9350604086013592506060860135915060808601356001600160401b03811115613d3f57600080fd5b6139de8882890161322a565b600060208284031215613d5d57600080fd5b8135612f3f8161348a565b6020808252600990820152682737ba1037bbb732b960b91b604082015260600190565b60005b83811015613da6578181015183820152602001613d8e565b50506000910152565b60008251613dc1818460208701613d8b565b9190910192915050565b600060208284031215613ddd57600080fd5b5051919050565b6020808252600c908201526b3737ba1037b832b930ba37b960a11b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201613e4857613e48613e20565b5060010190565b60208082526018908201527f536d61727420636f6e74726163742069732068616c7465640000000000000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b805160ff81168114610cac57600080fd5b600080600060608486031215613ee357600080fd5b613eec84613ebd565b925060208401519150604084015190509250925092565b6020808252600f908201526e504b206973206e6f7420726561647960881b604082015260600190565b60008151808452613f44816020860160208601613d8b565b601f01601f19169290920160200192915050565b82815260406020820152815160408201526020820151606082015260408201516080820152606082015160a0820152608082015160c082015260a082015160e0820152600060c0830151610100808185015250613fb9610140840182613f2c565b60e094909401516001600160a01b03166101209390930192909252509092915050565b6020808252600990820152683737ba1030b236b4b760b91b604082015260600190565b82815260406020820152815160408201526020820151606082015260408201516080820152606082015160a0820152608082015160c082015260a082015160e0820152600060c0830151610100818185015260e0850151915061012061406f818601846001600160a01b03169052565b8186015192506101409150818286015261408d610180860184613f2c565b908601516001600160a01b03811661016087015290925090505b50949350505050565b60006101408201905083825282516020830152602083015160408301526040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160018060a01b0380821660e085015260e085015191506101008183168186015280860151925050506141386101208401826001600160a01b03169052565b509392505050565b828152604060208201528151604082015260208201516060820152604082015160808201526000606083015160e060a0840152614181610120840182613740565b90506080840151603f198483030160c085015261419e8282613740565b60a08601516001600160a01b0390811660e087015260c0909601519095166101009094019390935250919392505050565b87815286602082015285604082015260e0606082015260006141f460e0830187613740565b82810360808401526142068187613740565b6001600160a01b0395861660a08501529390941660c090920191909152509695505050505050565b82815260406020820152815160408201526020820151606082015260006040830151610120806080850152614267610160850183613740565b91506060850151603f19808685030160a08701526142858483613740565b9350608087015160c087015260a087015160e087015260c08701519150610100828188015260e088015192508187860301848801526142c48584613f2c565b94508088015193505050506140a76101408501826001600160a01b03169052565b82815260406020820152815160408201526020820151606082015260408201516080820152600060608301516101008060a0850152614328610140850183613740565b91506080850151603f19808685030160c08701526143468483613740565b935060a08701519150808685030160e0870152506143648382613f2c565b92505060c0850151614380828601826001600160a01b03169052565b505060e08401516001600160a01b0381166101208501526140a7565b60006101008a83528960208401528860408401528060608401526143c281840189613740565b905082810360808401526143d68188613740565b905082810360a08401526143ea8187613f2c565b6001600160a01b0395861660c08501529390941660e09092019190915250979650505050505050565b6000806040838503121561442657600080fd5b8251614431816130fd565b6020939093015192949293505050565b8281526040602082015281516040820152602082015160608201526000604083015161014080608085015261447a610180850183613740565b91506060850151603f19808685030160a08701526144988483613740565b9350608087015160c087015260a087015160e087015260c08701519150610100828188015260e088015192506101206144db818901856001600160a01b03169052565b8189015193508288870301858901526144f48685613f2c565b9550808901519450505050506140a76101608501826001600160a01b03169052565b600082601f83011261452757600080fd5b815161453561324982613203565b81815284602083860101111561454a57600080fd5b61331f826020830160208701613d8b565b60008060008060008060008060008060006101608c8e03121561457d57600080fd5b8b519a5061458d60208d01613ebd565b995060408c0151985060608c0151975060808c0151965060a08c0151955060c08c0151945060e08c01516001600160401b038111156145cb57600080fd5b6145d78e828f01614516565b9450506101008c01516001600160401b038111156145f457600080fd5b6146008e828f01614516565b9350506101208c015191506101408c015190509295989b509295989b9093969950565b60006020828403121561463557600080fd5b8151612f3f8161348a565b80820180821115610ca657610ca6613e2056fea26469706673582212202292ee3d7aa19787413dc8c7498fa124ab91bfb1eee0e945cf36bf3ff4ac9d7164736f6c63430008120033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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