ETH Price: $3,320.97 (-4.23%)

Contract

0x5c9Eb2c169b0110C30c7adA7E53D76d1fCcFDeB4
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim Token214835902024-12-26 2:15:1115 hrs ago1735179311IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001208266.08156682
Claim Token214823862024-12-25 22:11:4719 hrs ago1735164707IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.000817634.12240745
Claim Token214721472024-12-24 11:51:472 days ago1735041107IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001529568.43941137
Claim Token214719892024-12-24 11:19:592 days ago1735039199IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.000760844.18987846
Claim Token214654172024-12-23 13:15:113 days ago1734959711IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001247477.58308918
Claim Token214567812024-12-22 8:15:354 days ago1734855335IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.000906915.90045568
Claim Token214566302024-12-22 7:45:234 days ago1734853523IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.000919585.98352493
Claim Token214525092024-12-21 17:55:115 days ago1734803711IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001197617.79179349
Claim Token214497862024-12-21 8:47:595 days ago1734770879IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001468858.95133069
Claim Token214494402024-12-21 7:37:595 days ago1734766679IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001395257.02239124
Claim Token214491682024-12-21 6:43:355 days ago1734763415IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001431048.69816849
Claim Token214486412024-12-21 4:57:355 days ago1734757055IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001395717.68601422
Claim Token214468462024-12-20 22:54:115 days ago1734735251IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.0015780810.29210444
Claim Token214272772024-12-18 5:17:358 days ago1734499055IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.0021133710.63482963
Claim Token214272432024-12-18 5:10:478 days ago1734498647IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.0021575810.85830308
Claim Token214270132024-12-18 4:24:358 days ago1734495875IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.0022082411.11156457
Claim Token214269632024-12-18 4:14:358 days ago1734495275IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.0021862311
Claim Token214211972024-12-17 8:55:359 days ago1734425735IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001632799.94772354
Claim Token214210352024-12-17 8:22:479 days ago1734423767IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.00170358.57219385
Claim Token214130222024-12-16 5:34:3510 days ago1734327275IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.0023865711.98110115
Claim Token214083182024-12-15 13:48:3511 days ago1734270515IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001557357.83731593
Claim Token214079422024-12-15 12:32:4711 days ago1734265967IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001137266.92996673
Claim Token214073652024-12-15 10:36:5911 days ago1734259019IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001093716.0220418
Claim Token214072102024-12-15 10:05:4711 days ago1734257147IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001183955.96936678
Claim Token214070932024-12-15 9:42:1111 days ago1734255731IN
0x5c9Eb2c1...1fCcFDeB4
0 ETH0.001250856.2961899
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
214835902024-12-26 2:15:1115 hrs ago1735179311
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214835902024-12-26 2:15:1115 hrs ago1735179311
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214823862024-12-25 22:11:4719 hrs ago1735164707
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214823862024-12-25 22:11:4719 hrs ago1735164707
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214721472024-12-24 11:51:472 days ago1735041107
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214721472024-12-24 11:51:472 days ago1735041107
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214719892024-12-24 11:19:592 days ago1735039199
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214719892024-12-24 11:19:592 days ago1735039199
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214654172024-12-23 13:15:113 days ago1734959711
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214654172024-12-23 13:15:113 days ago1734959711
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214567812024-12-22 8:15:354 days ago1734855335
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214567812024-12-22 8:15:354 days ago1734855335
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214566302024-12-22 7:45:234 days ago1734853523
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214566302024-12-22 7:45:234 days ago1734853523
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214525092024-12-21 17:55:115 days ago1734803711
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214525092024-12-21 17:55:115 days ago1734803711
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214497862024-12-21 8:47:595 days ago1734770879
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214497862024-12-21 8:47:595 days ago1734770879
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214494402024-12-21 7:37:595 days ago1734766679
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214494402024-12-21 7:37:595 days ago1734766679
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214491682024-12-21 6:43:355 days ago1734763415
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214491682024-12-21 6:43:355 days ago1734763415
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214486412024-12-21 4:57:355 days ago1734757055
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214486412024-12-21 4:57:355 days ago1734757055
0x5c9Eb2c1...1fCcFDeB4
0 ETH
214468462024-12-20 22:54:115 days ago1734735251
0x5c9Eb2c1...1fCcFDeB4
0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ApeVestingUnified

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion
File 1 of 32 : ApeVestingUnified.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {AxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol";
import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol";
import {IAxelarGasService} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol";
import "murky/Merkle.sol";

// forked from https://bscscan.com/address/0x17e994b2586f6a6059ba918078c5a3c52d41e03b#code
// this contract is an unified vesting contract for all IDOs.
// this will be deployed once for each chain

// #features
// - vesing contracts for all ido
// - refund supported with deadline
// - sync the list of investors cross chain
// - role based access (ROOT: cricital operation / ADMIN: management operation)

struct VestingProject {
    uint256 id;
    string name;
    /**
     * if `false`, user won't be able to claim or request refund
     */
    bool active;
    uint256 investors;
    bytes32 merkleProofRoot;
    // claim params
    uint256 tgeAt;
    uint256 tgeAmount;
    uint256 cliffDuration;
    uint256 vestingDuration;
    uint256 vestingAmount;
    address tokenAddr;
    uint256 tokenDeposited;
    uint256 tokenRemains;
    uint8 tokenDecimals;
    // refund params
    uint256 refundInvestors;
    uint256 refundDeadlineAt;
    address refundTokenAddr;
    uint256 refundAmount;
    uint8 refundTokenDecimals;
    uint256 refundTokenDeposited;
    uint256 refundTokenRemains;
}

contract ApeVestingUnified is
    Pausable,
    AccessControlEnumerable,
    AxelarExecutable,
    ReentrancyGuard
{
    using Address for address;
    using SafeERC20 for IERC20;
    using EnumerableSet for EnumerableSet.Bytes32Set;
    bytes32 public constant ROOT_ROLE = keccak256("ROOT_ROLE");
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
    // fix AVU-02S
    Merkle private mTree = new Merkle();
    // fix AVU-03S
    uint256 public projectIdCounter;
    IAxelarGasService public immutable gasService;
    // mpr = Merkle Proof Root
    EnumerableSet.Bytes32Set private s_trustedMprSource;
    mapping(uint256 => VestingProject) public s_project;
    mapping(uint256 projectId => mapping(address account => uint256 timestamp)) public s_refundRequestedAt;
    mapping(uint256 projectId => mapping(address account => uint256 timestamp)) public s_refundedAt;
    mapping(uint256 projectId => mapping(address account => uint256 amount)) public s_claimedAmount;

    constructor(
        address _gateway,
        address _gasReceiver
    ) AxelarExecutable(_gateway) {
        // fix AVU-01S
        assert(_gateway != address(0) && _gasReceiver != address(0));
        _grantRole(ROOT_ROLE, msg.sender);
        _setRoleAdmin(ROOT_ROLE, ROOT_ROLE);
        _setRoleAdmin(ADMIN_ROLE, ROOT_ROLE);
        gasService = IAxelarGasService(_gasReceiver);
    }

    // REGION: events

    event MerkleProofRootSet(uint256 indexed projectId, uint256 investors, bytes32 merkleRoot);
    event ProjectAdded(
        uint256 indexed projectId,
        string _name,
        uint256 _tgeAt,
        uint256 _tgeAmount,
        uint256 _cliffDuration,
        uint256 _vestingDuration,
        uint256 _vestingAmount,
        address _tokenAddr
    );
    event ProjectRefundParamsUpdated(
        uint256 indexed projectId,
        address refundTokenAddr,
        uint256 refundAmount,
        uint256 refundDeadlineAt,
        uint256 refundTokenDecimals
    );
    event TrustedMprSourceAdded(
        string sourceChain,
        string sourceAddress,
        bytes32 sourceId
    );
    event TrustedMprSourceRemoved(
        string sourceChain,
        string sourceAddress,
        bytes32 sourceId
    );
    event ProjectActiveStatusChanged(uint256 indexed projectId, bool activeStatus);
    event Claimed(
        uint256 indexed projectId,
        address indexed account,
        uint256 amount
    );
    event RefundRequested(uint256 indexed projectId, address indexed account);
    event Refunded(uint256 indexed projectId, address indexed account);
    event ProjectTokenDeposited(uint256 indexed projectId, uint256 amount, address funder);
    event ProjectRefundTokenDeposited(uint256 indexed projectId, uint256 amount, address funder);
    event UnusedTokenWithdrew(address indexed tokenAddr, uint256 amount, address receiver);
    event OrphanTokenWithdrew(uint256 projectId, address tokenAddr, uint256 amount, address receiver);

    // REGION: modifiers

    modifier whenProjectExist(uint256 projectId_) {
        require(s_project[projectId_].tokenAddr != address(0), "The Project does not exist");
        _;
    }

    modifier whenProjectActive(uint256 projectId_) {
        VestingProject memory sVProject = s_project[projectId_];
        require(sVProject.tokenAddr != address(0) && sVProject.active, "The Project does not active");
        _;
    }

    // REGION: internal functions

    function _setProjectMerkleRoot(
        uint256 projectId_,
        uint256 investors_,
        bytes32 merkleRoot_
    ) internal whenProjectExist(projectId_) {
        VestingProject storage sVProject = s_project[projectId_];
        require(sVProject.merkleProofRoot == bytes32(0), "Merkle proof root has been set");
        sVProject.investors = investors_;
        sVProject.merkleProofRoot = merkleRoot_;
        emit MerkleProofRootSet(projectId_, investors_, merkleRoot_);
    }

    // REGION: admin functions

    function addTrustedMprSource(
        string memory _sourceChain,
        string memory _sourceAddress
    ) public onlyRole(ROOT_ROLE) {
        bytes32 sourceId = keccak256(abi.encode(_sourceChain, _sourceAddress));
        if(!s_trustedMprSource.contains(sourceId)) {
            s_trustedMprSource.add(sourceId);
            emit TrustedMprSourceAdded(_sourceChain, _sourceAddress, sourceId);
        }
    }

    function removeTrustedMprSource(
        string memory _sourceChain,
        string memory _sourceAddress
    ) public onlyRole(ROOT_ROLE) {
        bytes32 sourceId = keccak256(abi.encode(_sourceChain, _sourceAddress));
        if(s_trustedMprSource.contains(sourceId)) {
            s_trustedMprSource.remove(sourceId);
            emit TrustedMprSourceRemoved(_sourceChain, _sourceAddress, sourceId);
        }
    }

    function removeTrustedMprSource(
        bytes32 _sourceId
    ) public onlyRole(ROOT_ROLE) {
        if(s_trustedMprSource.contains(_sourceId)) {
            s_trustedMprSource.remove(_sourceId);
            emit TrustedMprSourceRemoved("", "", _sourceId);
        }
    }

    function pause() public onlyRole(ADMIN_ROLE) {
        _pause();
    }

    function unpause() public onlyRole(ADMIN_ROLE) {
        _unpause();
    }

    /**
     * manually set Merkle Proof Root in case autosync via Axelar does not work
     */
    function setProjectMerkleRoot(
        uint256 projectId_,
        uint256 investors_,
        bytes32 merkleRoot_
    ) external onlyRole(ROOT_ROLE) {
        _setProjectMerkleRoot(projectId_, investors_, merkleRoot_);
    }

    /**
     * Set up new Project
     * - The vesting token must be already debut-ed
     * - Refund are not enabled by default
     * @param _tgeAt - the TGE date
     * @param _tgeAmount - the amount unlock at TGE date
     * @param _cliffDuration - the duration after TGE when no token will be unlocked
     * @param _vestingDuration - the duration that `vestingAmount` will be unlocked linearly 
     * @param _vestingAmount - 
     * @param _tokenAddr - the project's token
     */
    function addProject(
        string calldata _name,
        uint256 _tgeAt,
        uint256 _tgeAmount,
        uint256 _cliffDuration,
        uint256 _vestingDuration,
        uint256 _vestingAmount,
        address _tokenAddr
    ) public onlyRole(ADMIN_ROLE) returns (uint256 projectId) {
        // prevent adding unix time in milliseconds
        require(_tgeAt < 100_000_000_000, "5138-11-16T09:46:40.000Z");
        require(_vestingDuration > 0, "Assert: _vestingDuration > 0");
        require(_tokenAddr != address(0), "Assert: _tokenAddr != null");
        uint8 tokenDecimals = IERC20Metadata(_tokenAddr).decimals();
        require(tokenDecimals > 0, "Assert: tokenDecimals > 0");
        projectId = projectIdCounter++;
        s_project[projectId] = VestingProject({
            id: projectId,
            name: _name,
            active: true,
            investors: 0,
            tgeAt: _tgeAt,
            tgeAmount: _tgeAmount,
            cliffDuration: _cliffDuration,
            vestingDuration: _vestingDuration,
            vestingAmount: _vestingAmount,
            tokenAddr: _tokenAddr,
            tokenDeposited: 0,
            tokenRemains: 0,
            merkleProofRoot: bytes32(0),
            refundInvestors: 0,
            refundDeadlineAt: 0,
            refundTokenAddr: address(0),
            refundAmount: 0,
            tokenDecimals: tokenDecimals,
            refundTokenDecimals: 0,
            refundTokenDeposited: 0,
            refundTokenRemains: 0
        });
        emit ProjectAdded(
            projectId,
            _name,
            _tgeAt,
            _tgeAmount,
            _cliffDuration,
            _vestingDuration,
            _vestingAmount,
            _tokenAddr
        );
        return projectId;
    }

    function setProjectActiveStatus(uint256 projectId_, bool status_) public whenProjectExist(projectId_) onlyRole(ADMIN_ROLE) {
        s_project[projectId_].active = status_;
        emit ProjectActiveStatusChanged(projectId_, status_);
    }

    function setRefundParams(
        uint256 projectId_,
        address refundTokenAddr_,
        uint256 refundAmount_,
        uint256 refundDeadlineAt_
    ) public whenProjectExist(projectId_) onlyRole(ADMIN_ROLE) {
        VestingProject storage sVProject = s_project[projectId_];
        require(sVProject.refundTokenAddr == address(0), "Refund params has been set");
        uint8 refundTokenDecimals = IERC20Metadata(refundTokenAddr_).decimals();
        require(refundTokenDecimals > 0, "Assert: refundTokenDecimals > 0");
        require(refundAmount_ > 0, "Assert: refundAmount_ > 0");
        require(refundDeadlineAt_ > 0, "Assert: refundDeadlineAt_ > 0");
        require(refundTokenAddr_ != sVProject.tokenAddr, "Assert: refundTokenAddr != tokenAddr");
        sVProject.refundAmount = refundAmount_;
        sVProject.refundDeadlineAt = refundDeadlineAt_;
        sVProject.refundTokenAddr = refundTokenAddr_;
        sVProject.refundTokenDecimals = refundTokenDecimals;
        emit ProjectRefundParamsUpdated(
            projectId_,
            refundTokenAddr_,
            refundAmount_,
            refundDeadlineAt_,
            refundTokenDecimals
        );
    }

    /**
     * Allow ROOT user to withdraw the fund that has been sent to this contract by mistake
     * This function will fail if the number of projects is a big number like 5k
     */
    function withdrawUnusedToken(address tokenAddr_) external onlyRole(ROOT_ROLE) returns (uint256) {
        require(tokenAddr_ != address(0), "Assert: tokenAddr_ != null");
        uint256 unusedBalance = IERC20(tokenAddr_).balanceOf(address(this));
        // fix AVU-01C
        uint256 cachedProjectIdCounter = projectIdCounter;
        for(uint256 i=0; i<cachedProjectIdCounter; i++) {
            if(s_project[i].tokenAddr == tokenAddr_) {
                unusedBalance -= s_project[i].tokenRemains;
                continue;
            }
            if(s_project[i].refundTokenAddr == tokenAddr_) {
                unusedBalance -= s_project[i].refundTokenRemains;
            }
        }
        require(unusedBalance > 0, "Assert: unusedBalance > 0");
        IERC20(tokenAddr_).safeTransfer(
            msg.sender,
            unusedBalance
        );
        emit UnusedTokenWithdrew(tokenAddr_, unusedBalance, msg.sender);
        return unusedBalance;
    }

    /**
     * Allow ROOT user to withdraw the amount of token that is orphan
     */
    function withdrawOrphanToken(uint256 projectId_) external onlyRole(ROOT_ROLE) whenProjectExist(projectId_) returns(uint256) {
        VestingProject storage sVProject = s_project[projectId_];
        uint256 tgeAmount = sVProject.tgeAmount;
        uint256 vestingAmount = sVProject.vestingAmount;
        address tokenAddr = sVProject.tokenAddr;
        uint256 tokenDeposited = sVProject.tokenDeposited;
        uint256 refundDeadlineAt = sVProject.refundDeadlineAt;
        require(block.timestamp >= refundDeadlineAt, "Assert: block.timestamp >= refundDeadlineAt");
        uint256 investors = sVProject.investors;
        uint256 refundInvestors = sVProject.refundInvestors;
        require(refundInvestors > 0, "Assert: refundInvestors > 0");
        // orphan fund has been collected before
        require(tokenDeposited == investors * (tgeAmount + vestingAmount), "Assert: !orphanAmount");
        uint256 orphanAmount = refundInvestors * (tgeAmount + vestingAmount);
        sVProject.tokenRemains -= orphanAmount;
        sVProject.tokenDeposited -= orphanAmount;
        IERC20(tokenAddr).safeTransfer(
            msg.sender,
            orphanAmount
        );
        emit OrphanTokenWithdrew(projectId_, tokenAddr, orphanAmount, msg.sender);
        return orphanAmount;
    }

    // REGION: public view functions

    function getTrustedMprSources() public view returns (bytes32[] memory) {
        return s_trustedMprSource.values();
    }

    function isTrustedMprSource(bytes32 sourceId_) public view returns (bool) {
        return s_trustedMprSource.contains(sourceId_);
    }

    function getProject(uint256 projectId) public view returns (VestingProject memory) {
        return s_project[projectId];
    }

    /**
     * Return the stats for the account at specific project.
     * This function assume account has a valid investment proof
     */
    function getAccountStatsAt(
        uint256 projectId,
        address account
    )
        public
        view
        returns (
            uint256 refundRequestedAt,
            uint256 refundedAt,
            uint256 claimedAmount,
            uint256 claimableAmount
        )
    {
        refundRequestedAt = s_refundRequestedAt[projectId][account];
        refundedAt = s_refundedAt[projectId][account];
        claimedAmount = s_claimedAmount[projectId][account];
        uint256 tgeAt = s_project[projectId].tgeAt;
        uint256 tgeAmount = s_project[projectId].tgeAmount;
        uint256 vestingAmount = s_project[projectId].vestingAmount;
        uint256 cliffDuration = s_project[projectId].cliffDuration;
        uint256 vestingDuration = s_project[projectId].vestingDuration;
        if (block.timestamp < tgeAt) {
            claimableAmount = 0;
        } else {
            if (block.timestamp <= tgeAt + cliffDuration) {
                claimableAmount = tgeAmount - claimedAmount;
            } else {
                uint256 totalAmount = tgeAmount +
                    ((vestingAmount *
                        (block.timestamp - (tgeAt + cliffDuration))) /
                        vestingDuration);
                // fix AVU-05C
                if (totalAmount > vestingAmount + tgeAmount) {
                    totalAmount = vestingAmount + tgeAmount;
                }
                claimableAmount = totalAmount - claimedAmount;
            }
        }
        return (
            refundRequestedAt,
            // fix AVU-06C
            refundedAt,
            claimedAmount,
            claimableAmount
        );
    }

    // REGION: public write functions

    /**
     * called crosschain by Axelar
     */
    function _execute(
        string calldata _sourceChain,
        string calldata _sourceAddress,
        bytes calldata _payload
    ) internal override {
        bytes32 sourceId = keccak256(abi.encode(_sourceChain, _sourceAddress));
        require(s_trustedMprSource.contains(sourceId), "Untrusted mpr source");
        (uint256 projectId, uint256 investors, bytes32 merkleRoot) = abi.decode(
            _payload,
            (uint256, uint256, bytes32)
        );
        _setProjectMerkleRoot(projectId, investors, merkleRoot);
    }

    /**
     * allow user to request a refund if they has not claimed yet
     */
    function requestRefund(
        uint256 projectId_,
        bytes32[] memory proof
    ) external whenNotPaused nonReentrant whenProjectActive(projectId_) {
        address sender = msg.sender;
        VestingProject storage sVProject = s_project[projectId_];
        bytes32 merkleProofRoot = sVProject.merkleProofRoot;
        address refundTokenAddr = sVProject.refundTokenAddr;
        require(
            merkleProofRoot != bytes32(0),
            "Merkle proof root has not been set"
        );
        require(
            refundTokenAddr != address(0),
            "Refund params has not been set"
        );
        require(
            block.timestamp < sVProject.refundDeadlineAt,
            "Refund deadline has passed"
        );
        require(
            s_refundRequestedAt[projectId_][sender] == 0,
            "Refund requested"
        );
        require(
            s_claimedAmount[projectId_][sender] == 0,
            "Not eligible for a refund (claimed)"
        );
        require(
            mTree.verifyProof(
                merkleProofRoot,
                proof,
                bytes32(abi.encode(sender))
            ),
            "Mismatch investment proof"
        );
        s_refundRequestedAt[projectId_][sender] = block.timestamp;
        sVProject.refundInvestors += 1;
        emit RefundRequested(projectId_, sender);
    }

    function claimToken(
        uint256 projectId_,
        bytes32[] calldata proof_
    ) external whenNotPaused nonReentrant whenProjectActive(projectId_) {
        address sender = msg.sender;
        VestingProject storage sVProject = s_project[projectId_];
        bytes32 merkleProofRoot = sVProject.merkleProofRoot;
        require(
            merkleProofRoot != bytes32(0),
            "Merkle proof root has not been set"
        );
        require(
            mTree.verifyProof(
                merkleProofRoot,
                proof_,
                bytes32(abi.encode(sender))
            ),
            "Mismatch investment proof"
        );
        require(
            s_refundRequestedAt[projectId_][sender] == 0,
            "Not eligible for a claim (refund requested)"
        );
        (, , , uint256 claimableAmount) = getAccountStatsAt(projectId_, sender);
        require(claimableAmount > 0, "No claimable tokens");
        s_claimedAmount[projectId_][sender] += claimableAmount;
        sVProject.tokenRemains -= claimableAmount;
        IERC20(sVProject.tokenAddr).safeTransfer(
            sender,
            claimableAmount
        );
        emit Claimed(projectId_, sender, claimableAmount);
    }

    function claimRefundToken(
        uint256 projectId_
    ) external whenNotPaused nonReentrant whenProjectActive(projectId_) {
        address sender = msg.sender;
        VestingProject storage sVProject = s_project[projectId_];
        uint256 refundAmount = sVProject.refundAmount;
        address refundTokenAddr = sVProject.refundTokenAddr;
        require(
            block.timestamp >= sVProject.refundDeadlineAt,
            "Refund deadline has not passed"
        );
        require(
            s_refundRequestedAt[projectId_][sender] != 0,
            "Not eligible for a refund (unrequested)"
        );
        require(
            s_refundedAt[projectId_][sender] == 0,
            "Not eligible for a refund (refunded)"
        );
        s_refundedAt[projectId_][sender] = block.timestamp;
        sVProject.refundTokenRemains -= refundAmount;
        IERC20(refundTokenAddr).safeTransfer(
            sender,
            refundAmount
        );
        emit Refunded(
            projectId_,
            sender
        );
    }

    /**
     * allow anyone to deposit the project's token
     */
    function depositProjectToken(uint256 projectId_) public whenProjectExist(projectId_) {
        VestingProject storage sVProject = s_project[projectId_];
        uint256 investors = sVProject.investors;
        uint256 tgeAmount = sVProject.tgeAmount;
        uint256 vestingAmount = sVProject.vestingAmount;
        address tokenAddr = sVProject.tokenAddr;
        require(sVProject.tokenDeposited == 0, "Assert: tokenDeposited = 0");
        // this also mean the merkle proof root has been set
        require(investors > 0, "Assert: investors > 0");
        uint256 depositAmount = (tgeAmount + vestingAmount) * investors;
        sVProject.tokenDeposited = depositAmount;
        sVProject.tokenRemains = depositAmount;
        IERC20(tokenAddr).safeTransferFrom(
            msg.sender,
            address(this),
            depositAmount
        );
        emit ProjectTokenDeposited(projectId_, depositAmount, msg.sender);
    }

    /**
     * allow anyone to deposit the project's refund token after the refund deadline
     */
    function depositProjectRefundToken(uint256 projectId_) public whenProjectExist(projectId_) {
        VestingProject storage sVProject = s_project[projectId_];
        uint256 refundInvestors = sVProject.refundInvestors;
        uint256 refundAmount = sVProject.refundAmount;
        address refundTokenAddr = sVProject.refundTokenAddr;
        require(sVProject.refundTokenDeposited == 0, "Assert: refundTokenDeposited = 0");
        uint256 refundDeadlineAt = sVProject.refundDeadlineAt;
        require(block.timestamp >= refundDeadlineAt, "Refund deadline has not passed");
        // this also mean the merkle proof root has been set
        require(refundInvestors > 0, "Assert: refundInvestors > 0");
        uint256 depositAmount = refundAmount * refundInvestors;
        sVProject.refundTokenDeposited = depositAmount;
        sVProject.refundTokenRemains = depositAmount;
        IERC20(refundTokenAddr).safeTransferFrom(
            msg.sender,
            address(this),
            depositAmount
        );
        emit ProjectRefundTokenDeposited(projectId_, depositAmount, msg.sender);
    }

}

File 2 of 32 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 3 of 32 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

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

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

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

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

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

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

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

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

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

File 6 of 32 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 7 of 32 : AccessControlEnumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Overload {_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        super._grantRole(role, account);
        _roleMembers[role].add(account);
    }

    /**
     * @dev Overload {_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        super._revokeRole(role, account);
        _roleMembers[role].remove(account);
    }
}

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

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

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

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

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

File 9 of 32 : AxelarExecutable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IAxelarGateway } from '../interfaces/IAxelarGateway.sol';
import { IAxelarExecutable } from '../interfaces/IAxelarExecutable.sol';

contract AxelarExecutable is IAxelarExecutable {
    IAxelarGateway public immutable gateway;

    constructor(address gateway_) {
        if (gateway_ == address(0)) revert InvalidAddress();

        gateway = IAxelarGateway(gateway_);
    }

    function execute(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) external {
        bytes32 payloadHash = keccak256(payload);

        if (!gateway.validateContractCall(commandId, sourceChain, sourceAddress, payloadHash))
            revert NotApprovedByGateway();

        _execute(sourceChain, sourceAddress, payload);
    }

    function executeWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload,
        string calldata tokenSymbol,
        uint256 amount
    ) external {
        bytes32 payloadHash = keccak256(payload);

        if (
            !gateway.validateContractCallAndMint(
                commandId,
                sourceChain,
                sourceAddress,
                payloadHash,
                tokenSymbol,
                amount
            )
        ) revert NotApprovedByGateway();

        _executeWithToken(sourceChain, sourceAddress, payload, tokenSymbol, amount);
    }

    function _execute(
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) internal virtual {}

    function _executeWithToken(
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload,
        string calldata tokenSymbol,
        uint256 amount
    ) internal virtual {}
}

File 10 of 32 : IAxelarGateway.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IGovernable } from './IGovernable.sol';
import { IImplementation } from './IImplementation.sol';

interface IAxelarGateway is IImplementation, IGovernable {
    /**********\
    |* Errors *|
    \**********/

    error NotSelf();
    error InvalidCodeHash();
    error SetupFailed();
    error InvalidAuthModule();
    error InvalidTokenDeployer();
    error InvalidAmount();
    error InvalidChainId();
    error InvalidCommands();
    error TokenDoesNotExist(string symbol);
    error TokenAlreadyExists(string symbol);
    error TokenDeployFailed(string symbol);
    error TokenContractDoesNotExist(address token);
    error BurnFailed(string symbol);
    error MintFailed(string symbol);
    error InvalidSetMintLimitsParams();
    error ExceedMintLimit(string symbol);

    /**********\
    |* Events *|
    \**********/

    event TokenSent(
        address indexed sender,
        string destinationChain,
        string destinationAddress,
        string symbol,
        uint256 amount
    );

    event ContractCall(
        address indexed sender,
        string destinationChain,
        string destinationContractAddress,
        bytes32 indexed payloadHash,
        bytes payload
    );

    event ContractCallWithToken(
        address indexed sender,
        string destinationChain,
        string destinationContractAddress,
        bytes32 indexed payloadHash,
        bytes payload,
        string symbol,
        uint256 amount
    );

    event Executed(bytes32 indexed commandId);

    event TokenDeployed(string symbol, address tokenAddresses);

    event ContractCallApproved(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        address indexed contractAddress,
        bytes32 indexed payloadHash,
        bytes32 sourceTxHash,
        uint256 sourceEventIndex
    );

    event ContractCallApprovedWithMint(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        address indexed contractAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        bytes32 sourceTxHash,
        uint256 sourceEventIndex
    );

    event ContractCallExecuted(bytes32 indexed commandId);

    event TokenMintLimitUpdated(string symbol, uint256 limit);

    event OperatorshipTransferred(bytes newOperatorsData);

    event Upgraded(address indexed implementation);

    /********************\
    |* Public Functions *|
    \********************/

    function sendToken(
        string calldata destinationChain,
        string calldata destinationAddress,
        string calldata symbol,
        uint256 amount
    ) external;

    function callContract(
        string calldata destinationChain,
        string calldata contractAddress,
        bytes calldata payload
    ) external;

    function callContractWithToken(
        string calldata destinationChain,
        string calldata contractAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount
    ) external;

    function isContractCallApproved(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        address contractAddress,
        bytes32 payloadHash
    ) external view returns (bool);

    function isContractCallAndMintApproved(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        address contractAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) external view returns (bool);

    function validateContractCall(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash
    ) external returns (bool);

    function validateContractCallAndMint(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) external returns (bool);

    /***********\
    |* Getters *|
    \***********/

    function authModule() external view returns (address);

    function tokenDeployer() external view returns (address);

    function tokenMintLimit(string memory symbol) external view returns (uint256);

    function tokenMintAmount(string memory symbol) external view returns (uint256);

    function allTokensFrozen() external view returns (bool);

    function implementation() external view returns (address);

    function tokenAddresses(string memory symbol) external view returns (address);

    function tokenFrozen(string memory symbol) external view returns (bool);

    function isCommandExecuted(bytes32 commandId) external view returns (bool);

    /************************\
    |* Governance Functions *|
    \************************/

    function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external;

    function upgrade(
        address newImplementation,
        bytes32 newImplementationCodeHash,
        bytes calldata setupParams
    ) external;

    /**********************\
    |* External Functions *|
    \**********************/

    function execute(bytes calldata input) external;
}

File 11 of 32 : IAxelarGasService.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { GasInfo } from '../types/GasEstimationTypes.sol';
import { IInterchainGasEstimation } from './IInterchainGasEstimation.sol';
import { IUpgradable } from './IUpgradable.sol';

/**
 * @title IAxelarGasService Interface
 * @notice This is an interface for the AxelarGasService contract which manages gas payments
 * and refunds for cross-chain communication on the Axelar network.
 * @dev This interface inherits IUpgradable
 */
interface IAxelarGasService is IInterchainGasEstimation, IUpgradable {
    error InvalidAddress();
    error NotCollector();
    error InvalidAmounts();
    error InvalidGasUpdates();
    error InvalidParams();
    error InsufficientGasPayment(uint256 required, uint256 provided);

    event GasPaidForContractCall(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event GasPaidForContractCallWithToken(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeGasPaidForContractCall(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeGasPaidForContractCallWithToken(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event GasPaidForExpressCall(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event GasPaidForExpressCallWithToken(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeGasPaidForExpressCall(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeGasPaidForExpressCallWithToken(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event GasAdded(
        bytes32 indexed txHash,
        uint256 indexed logIndex,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress);

    event ExpressGasAdded(
        bytes32 indexed txHash,
        uint256 indexed logIndex,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeExpressGasAdded(
        bytes32 indexed txHash,
        uint256 indexed logIndex,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event Refunded(
        bytes32 indexed txHash,
        uint256 indexed logIndex,
        address payable receiver,
        address token,
        uint256 amount
    );

    /**
     * @notice Pay for gas for any type of contract execution on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @dev If estimateOnChain is true, the function will estimate the gas cost and revert if the payment is insufficient.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call
     * @param executionGasLimit The gas limit for the contract call
     * @param estimateOnChain Flag to enable on-chain gas estimation
     * @param refundAddress The address where refunds, if any, should be sent
     * @param params Additional parameters for gas payment. This can be left empty for normal contract call payments.
     */
    function payGas(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        uint256 executionGasLimit,
        bool estimateOnChain,
        address refundAddress,
        bytes calldata params
    ) external payable;

    /**
     * @notice Pay for gas using ERC20 tokens for a contract call on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call
     * @param gasToken The address of the ERC20 token used to pay for gas
     * @param gasFeeAmount The amount of tokens to pay for gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payGasForContractCall(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Pay for gas using ERC20 tokens for a contract call with tokens on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call with tokens will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call with tokens
     * @param symbol The symbol of the token to be sent with the call
     * @param amount The amount of tokens to be sent with the call
     * @param gasToken The address of the ERC20 token used to pay for gas
     * @param gasFeeAmount The amount of tokens to pay for gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payGasForContractCallWithToken(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Pay for gas using native currency for a contract call on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payNativeGasForContractCall(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundAddress
    ) external payable;

    /**
     * @notice Pay for gas using native currency for a contract call with tokens on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call with tokens will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call with tokens
     * @param symbol The symbol of the token to be sent with the call
     * @param amount The amount of tokens to be sent with the call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payNativeGasForContractCallWithToken(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount,
        address refundAddress
    ) external payable;

    /**
     * @notice Pay for gas using ERC20 tokens for an express contract call on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to express execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call
     * @param gasToken The address of the ERC20 token used to pay for gas
     * @param gasFeeAmount The amount of tokens to pay for gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payGasForExpressCall(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Pay for gas using ERC20 tokens for an express contract call with tokens on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to express execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call with tokens will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call with tokens
     * @param symbol The symbol of the token to be sent with the call
     * @param amount The amount of tokens to be sent with the call
     * @param gasToken The address of the ERC20 token used to pay for gas
     * @param gasFeeAmount The amount of tokens to pay for gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payGasForExpressCallWithToken(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Pay for gas using native currency for an express contract call on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payNativeGasForExpressCall(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundAddress
    ) external payable;

    /**
     * @notice Pay for gas using native currency for an express contract call with tokens on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call with tokens will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call with tokens
     * @param symbol The symbol of the token to be sent with the call
     * @param amount The amount of tokens to be sent with the call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payNativeGasForExpressCallWithToken(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount,
        address refundAddress
    ) external payable;

    /**
     * @notice Add additional gas payment using ERC20 tokens after initiating a cross-chain call.
     * @dev This function can be called on the source chain after calling the gateway to execute a remote contract.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param gasToken The ERC20 token address used to add gas
     * @param gasFeeAmount The amount of tokens to add as gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function addGas(
        bytes32 txHash,
        uint256 logIndex,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Add additional gas payment using native currency after initiating a cross-chain call.
     * @dev This function can be called on the source chain after calling the gateway to execute a remote contract.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function addNativeGas(
        bytes32 txHash,
        uint256 logIndex,
        address refundAddress
    ) external payable;

    /**
     * @notice Add additional gas payment using ERC20 tokens after initiating an express cross-chain call.
     * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param gasToken The ERC20 token address used to add gas
     * @param gasFeeAmount The amount of tokens to add as gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function addExpressGas(
        bytes32 txHash,
        uint256 logIndex,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Add additional gas payment using native currency after initiating an express cross-chain call.
     * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function addNativeExpressGas(
        bytes32 txHash,
        uint256 logIndex,
        address refundAddress
    ) external payable;

    /**
     * @notice Updates the gas price for a specific chain.
     * @dev This function is called by the gas oracle to update the gas prices for a specific chains.
     * @param chains Array of chain names
     * @param gasUpdates Array of gas updates
     */
    function updateGasInfo(string[] calldata chains, GasInfo[] calldata gasUpdates) external;

    /**
     * @notice Allows the gasCollector to collect accumulated fees from the contract.
     * @dev Use address(0) as the token address for native currency.
     * @param receiver The address to receive the collected fees
     * @param tokens Array of token addresses to be collected
     * @param amounts Array of amounts to be collected for each respective token address
     */
    function collectFees(
        address payable receiver,
        address[] calldata tokens,
        uint256[] calldata amounts
    ) external;

    /**
     * @notice Refunds gas payment to the receiver in relation to a specific cross-chain transaction.
     * @dev Only callable by the gasCollector.
     * @dev Use address(0) as the token address to refund native currency.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param receiver The address to receive the refund
     * @param token The token address to be refunded
     * @param amount The amount to refund
     */
    function refund(
        bytes32 txHash,
        uint256 logIndex,
        address payable receiver,
        address token,
        uint256 amount
    ) external;

    /**
     * @notice Returns the address of the designated gas collector.
     * @return address of the gas collector
     */
    function gasCollector() external returns (address);
}

File 12 of 32 : Merkle.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./common/MurkyBase.sol";

/// @notice Nascent, simple, kinda efficient (and improving!) Merkle proof generator and verifier
/// @author dmfxyz
/// @dev Note Generic Merkle Tree
contract Merkle is MurkyBase {
    /**
     *
     * HASHING FUNCTION *
     *
     */

    /// ascending sort and concat prior to hashing
    function hashLeafPairs(bytes32 left, bytes32 right) public pure override returns (bytes32 _hash) {
        assembly {
            switch lt(left, right)
            case 0 {
                mstore(0x0, right)
                mstore(0x20, left)
            }
            default {
                mstore(0x0, left)
                mstore(0x20, right)
            }
            _hash := keccak256(0x0, 0x40)
        }
    }
}

File 13 of 32 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 14 of 32 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 15 of 32 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 16 of 32 : IAccessControlEnumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

File 17 of 32 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 18 of 32 : IAxelarExecutable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

interface IAxelarExecutable {
    error InvalidAddress();
    error NotApprovedByGateway();

    function gateway() external view returns (IAxelarGateway);

    function execute(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) external;

    function executeWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload,
        string calldata tokenSymbol,
        uint256 amount
    ) external;
}

File 19 of 32 : IGovernable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title IGovernable Interface
 * @notice This is an interface used by the AxelarGateway contract to manage governance and mint limiter roles.
 */
interface IGovernable {
    error NotGovernance();
    error NotMintLimiter();
    error InvalidGovernance();
    error InvalidMintLimiter();

    event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance);
    event MintLimiterTransferred(address indexed previousGovernance, address indexed newGovernance);

    /**
     * @notice Returns the governance address.
     * @return address of the governance
     */
    function governance() external view returns (address);

    /**
     * @notice Returns the mint limiter address.
     * @return address of the mint limiter
     */
    function mintLimiter() external view returns (address);

    /**
     * @notice Transfer the governance role to another address.
     * @param newGovernance The new governance address
     */
    function transferGovernance(address newGovernance) external;

    /**
     * @notice Transfer the mint limiter role to another address.
     * @param newGovernance The new mint limiter address
     */
    function transferMintLimiter(address newGovernance) external;
}

File 20 of 32 : IImplementation.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

interface IImplementation is IContractIdentifier {
    error NotProxy();

    function setup(bytes calldata data) external;
}

File 21 of 32 : GasEstimationTypes.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title GasEstimationType
 * @notice This enum represents the gas estimation types for different chains.
 */
enum GasEstimationType {
    Default,
    OptimismEcotone,
    OptimismBedrock,
    Arbitrum,
    Scroll
}

/**
 * @title GasInfo
 * @notice This struct represents the gas pricing information for a specific chain.
 * @dev Smaller uint types are used for efficient struct packing to save storage costs.
 */
struct GasInfo {
    /// @dev Custom gas pricing rule, such as L1 data fee on L2s
    uint64 gasEstimationType;
    /// @dev Scalar value needed for specific gas estimation types, expected to be less than 1e10
    uint64 l1FeeScalar;
    /// @dev Axelar base fee for cross-chain message approval on destination, in terms of source native gas token
    uint128 axelarBaseFee;
    /// @dev Gas price of destination chain, in terms of the source chain token, i.e dest_gas_price * dest_token_market_price / src_token_market_price
    uint128 relativeGasPrice;
    /// @dev Needed for specific gas estimation types. Blob base fee of destination chain, in terms of the source chain token, i.e dest_blob_base_fee * dest_token_market_price / src_token_market_price
    uint128 relativeBlobBaseFee;
    /// @dev Axelar express fee for express execution, in terms of source chain token
    uint128 expressFee;
}

File 22 of 32 : IInterchainGasEstimation.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol';

/**
 * @title IInterchainGasEstimation Interface
 * @notice This is an interface for the InterchainGasEstimation contract
 * which allows for estimating gas fees for cross-chain communication on the Axelar network.
 */
interface IInterchainGasEstimation {
    error UnsupportedEstimationType(GasEstimationType gasEstimationType);

    /**
     * @notice Event emitted when the gas price for a specific chain is updated.
     * @param chain The name of the chain
     * @param info The gas info for the chain
     */
    event GasInfoUpdated(string chain, GasInfo info);

    /**
     * @notice Returns the gas price for a specific chain.
     * @param chain The name of the chain
     * @return gasInfo The gas info for the chain
     */
    function getGasInfo(string calldata chain) external view returns (GasInfo memory);

    /**
     * @notice Estimates the gas fee for a cross-chain contract call.
     * @param destinationChain Axelar registered name of the destination chain
     * @param destinationAddress Destination contract address being called
     * @param executionGasLimit The gas limit to be used for the destination contract execution,
     *        e.g. pass in 200k if your app consumes needs upto 200k for this contract call
     * @param params Additional parameters for the gas estimation
     * @return gasEstimate The cross-chain gas estimate, in terms of source chain's native gas token that should be forwarded to the gas service.
     */
    function estimateGasFee(
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        uint256 executionGasLimit,
        bytes calldata params
    ) external view returns (uint256 gasEstimate);
}

File 23 of 32 : IUpgradable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IOwnable } from './IOwnable.sol';
import { IImplementation } from './IImplementation.sol';

// General interface for upgradable contracts
interface IUpgradable is IOwnable, IImplementation {
    error InvalidCodeHash();
    error InvalidImplementation();
    error SetupFailed();

    event Upgraded(address indexed newImplementation);

    function implementation() external view returns (address);

    function upgrade(
        address newImplementation,
        bytes32 newImplementationCodeHash,
        bytes calldata params
    ) external;
}

File 24 of 32 : MurkyBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

abstract contract MurkyBase {
    /**
     *
     * CONSTRUCTOR *
     *
     */
    constructor() {}

    /**
     *
     * VIRTUAL HASHING FUNCTIONS *
     *
     */
    function hashLeafPairs(bytes32 left, bytes32 right) public pure virtual returns (bytes32 _hash);

    /**
     *
     * PROOF VERIFICATION *
     *
     */
    function verifyProof(bytes32 root, bytes32[] memory proof, bytes32 valueToProve)
        external
        pure
        virtual
        returns (bool)
    {
        // proof length must be less than max array size
        bytes32 rollingHash = valueToProve;
        uint256 length = proof.length;
        unchecked {
            for (uint256 i = 0; i < length; ++i) {
                rollingHash = hashLeafPairs(rollingHash, proof[i]);
            }
        }
        return root == rollingHash;
    }

    /**
     *
     * PROOF GENERATION *
     *
     */
    function getRoot(bytes32[] memory data) public pure virtual returns (bytes32) {
        require(data.length > 1, "won't generate root for single leaf");
        while (data.length > 1) {
            data = hashLevel(data);
        }
        return data[0];
    }

    function getProof(bytes32[] memory data, uint256 node) public pure virtual returns (bytes32[] memory) {
        require(data.length > 1, "won't generate proof for single leaf");
        // The size of the proof is equal to the ceiling of log2(numLeaves)
        bytes32[] memory result = new bytes32[](log2ceilBitMagic(data.length));
        uint256 pos = 0;

        // Two overflow risks: node, pos
        // node: max array size is 2**256-1. Largest index in the array will be 1 less than that. Also,
        // for dynamic arrays, size is limited to 2**64-1
        // pos: pos is bounded by log2(data.length), which should be less than type(uint256).max
        while (data.length > 1) {
            unchecked {
                if (node & 0x1 == 1) {
                    result[pos] = data[node - 1];
                } else if (node + 1 == data.length) {
                    result[pos] = bytes32(0);
                } else {
                    result[pos] = data[node + 1];
                }
                ++pos;
                node /= 2;
            }
            data = hashLevel(data);
        }
        return result;
    }

    ///@dev function is private to prevent unsafe data from being passed
    function hashLevel(bytes32[] memory data) private pure returns (bytes32[] memory) {
        bytes32[] memory result;

        // Function is private, and all internal callers check that data.length >=2.
        // Underflow is not possible as lowest possible value for data/result index is 1
        // overflow should be safe as length is / 2 always.
        unchecked {
            uint256 length = data.length;
            if (length & 0x1 == 1) {
                result = new bytes32[](length / 2 + 1);
                result[result.length - 1] = hashLeafPairs(data[length - 1], bytes32(0));
            } else {
                result = new bytes32[](length / 2);
            }
            // pos is upper bounded by data.length / 2, so safe even if array is at max size
            uint256 pos = 0;
            for (uint256 i = 0; i < length - 1; i += 2) {
                result[pos] = hashLeafPairs(data[i], data[i + 1]);
                ++pos;
            }
        }
        return result;
    }

    /**
     *
     * MATH "LIBRARY" *
     *
     */

    /// @dev  Note that x is assumed > 0
    function log2ceil(uint256 x) public pure returns (uint256) {
        uint256 ceil = 0;
        uint256 pOf2;
        // If x is a power of 2, then this function will return a ceiling
        // that is 1 greater than the actual ceiling. So we need to check if
        // x is a power of 2, and subtract one from ceil if so.
        assembly {
            // we check by seeing if x == (~x + 1) & x. This applies a mask
            // to find the lowest set bit of x and then checks it for equality
            // with x. If they are equal, then x is a power of 2.

            /* Example
                x has single bit set
                x := 0000_1000
                (~x + 1) = (1111_0111) + 1 = 1111_1000
                (1111_1000 & 0000_1000) = 0000_1000 == x

                x has multiple bits set
                x := 1001_0010
                (~x + 1) = (0110_1101 + 1) = 0110_1110
                (0110_1110 & x) = 0000_0010 != x
            */

            // we do some assembly magic to treat the bool as an integer later on
            pOf2 := eq(and(add(not(x), 1), x), x)
        }

        // if x == type(uint256).max, than ceil is capped at 256
        // if x == 0, then pO2 == 0, so ceil won't underflow
        unchecked {
            while (x > 0) {
                x >>= 1;
                ceil++;
            }
            ceil -= pOf2; // see above
        }
        return ceil;
    }

    /// Original bitmagic adapted from https://github.com/paulrberg/prb-math/blob/main/contracts/PRBMath.sol
    /// @dev Note that x assumed > 1
    function log2ceilBitMagic(uint256 x) public pure returns (uint256) {
        if (x <= 1) {
            return 0;
        }
        uint256 msb = 0;
        uint256 _x = x;
        if (x >= 2 ** 128) {
            x >>= 128;
            msb += 128;
        }
        if (x >= 2 ** 64) {
            x >>= 64;
            msb += 64;
        }
        if (x >= 2 ** 32) {
            x >>= 32;
            msb += 32;
        }
        if (x >= 2 ** 16) {
            x >>= 16;
            msb += 16;
        }
        if (x >= 2 ** 8) {
            x >>= 8;
            msb += 8;
        }
        if (x >= 2 ** 4) {
            x >>= 4;
            msb += 4;
        }
        if (x >= 2 ** 2) {
            x >>= 2;
            msb += 2;
        }
        if (x >= 2 ** 1) {
            msb += 1;
        }

        uint256 lsb = (~_x + 1) & _x;
        if ((lsb == _x) && (msb > 0)) {
            return msb;
        } else {
            return msb + 1;
        }
    }
}

File 25 of 32 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

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

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

File 27 of 32 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 28 of 32 : IContractIdentifier.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// General interface for upgradable contracts
interface IContractIdentifier {
    /**
     * @notice Returns the contract ID. It can be used as a check during upgrades.
     * @dev Meant to be overridden in derived contracts.
     * @return bytes32 The contract ID
     */
    function contractId() external pure returns (bytes32);
}

File 29 of 32 : IOwnable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title IOwnable Interface
 * @notice IOwnable is an interface that abstracts the implementation of a
 * contract with ownership control features. It's commonly used in upgradable
 * contracts and includes the functionality to get current owner, transfer
 * ownership, and propose and accept ownership.
 */
interface IOwnable {
    error NotOwner();
    error InvalidOwner();
    error InvalidOwnerAddress();

    event OwnershipTransferStarted(address indexed newOwner);
    event OwnershipTransferred(address indexed newOwner);

    /**
     * @notice Returns the current owner of the contract.
     * @return address The address of the current owner
     */
    function owner() external view returns (address);

    /**
     * @notice Returns the address of the pending owner of the contract.
     * @return address The address of the pending owner
     */
    function pendingOwner() external view returns (address);

    /**
     * @notice Transfers ownership of the contract to a new address
     * @param newOwner The address to transfer ownership to
     */
    function transferOwnership(address newOwner) external;

    /**
     * @notice Proposes to transfer the contract's ownership to a new address.
     * The new owner needs to accept the ownership explicitly.
     * @param newOwner The address to transfer ownership to
     */
    function proposeOwnership(address newOwner) external;

    /**
     * @notice Transfers ownership to the pending owner.
     * @dev Can only be called by the pending owner
     */
    function acceptOwnership() external;
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

Settings
{
  "remappings": [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "chainlink/=lib/foundry-chainlink-toolkit/lib/chainlink-brownie-contracts/",
    "murky/=lib/murky/src/",
    "@axelar-network/axelar-gmp-sdk-solidity/=lib/axelar-gmp-sdk-solidity/",
    "axelar-gmp-sdk-solidity/=lib/axelar-gmp-sdk-solidity/contracts/",
    "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_gateway","type":"address"},{"internalType":"address","name":"_gasReceiver","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"NotApprovedByGateway","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"investors","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"name":"MerkleProofRootSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"OrphanTokenWithdrew","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"activeStatus","type":"bool"}],"name":"ProjectActiveStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"string","name":"_name","type":"string"},{"indexed":false,"internalType":"uint256","name":"_tgeAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_tgeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_cliffDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_vestingDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_vestingAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"_tokenAddr","type":"address"}],"name":"ProjectAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"address","name":"refundTokenAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"refundAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"refundDeadlineAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"refundTokenDecimals","type":"uint256"}],"name":"ProjectRefundParamsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"funder","type":"address"}],"name":"ProjectRefundTokenDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"funder","type":"address"}],"name":"ProjectTokenDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"RefundRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"Refunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"sourceChain","type":"string"},{"indexed":false,"internalType":"string","name":"sourceAddress","type":"string"},{"indexed":false,"internalType":"bytes32","name":"sourceId","type":"bytes32"}],"name":"TrustedMprSourceAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"sourceChain","type":"string"},{"indexed":false,"internalType":"string","name":"sourceAddress","type":"string"},{"indexed":false,"internalType":"bytes32","name":"sourceId","type":"bytes32"}],"name":"TrustedMprSourceRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"UnusedTokenWithdrew","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROOT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"uint256","name":"_tgeAt","type":"uint256"},{"internalType":"uint256","name":"_tgeAmount","type":"uint256"},{"internalType":"uint256","name":"_cliffDuration","type":"uint256"},{"internalType":"uint256","name":"_vestingDuration","type":"uint256"},{"internalType":"uint256","name":"_vestingAmount","type":"uint256"},{"internalType":"address","name":"_tokenAddr","type":"address"}],"name":"addProject","outputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_sourceChain","type":"string"},{"internalType":"string","name":"_sourceAddress","type":"string"}],"name":"addTrustedMprSource","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId_","type":"uint256"}],"name":"claimRefundToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId_","type":"uint256"},{"internalType":"bytes32[]","name":"proof_","type":"bytes32[]"}],"name":"claimToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId_","type":"uint256"}],"name":"depositProjectRefundToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId_","type":"uint256"}],"name":"depositProjectToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commandId","type":"bytes32"},{"internalType":"string","name":"sourceChain","type":"string"},{"internalType":"string","name":"sourceAddress","type":"string"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commandId","type":"bytes32"},{"internalType":"string","name":"sourceChain","type":"string"},{"internalType":"string","name":"sourceAddress","type":"string"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"executeWithToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gasService","outputs":[{"internalType":"contract IAxelarGasService","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gateway","outputs":[{"internalType":"contract IAxelarGateway","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"getAccountStatsAt","outputs":[{"internalType":"uint256","name":"refundRequestedAt","type":"uint256"},{"internalType":"uint256","name":"refundedAt","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"}],"name":"getProject","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"investors","type":"uint256"},{"internalType":"bytes32","name":"merkleProofRoot","type":"bytes32"},{"internalType":"uint256","name":"tgeAt","type":"uint256"},{"internalType":"uint256","name":"tgeAmount","type":"uint256"},{"internalType":"uint256","name":"cliffDuration","type":"uint256"},{"internalType":"uint256","name":"vestingDuration","type":"uint256"},{"internalType":"uint256","name":"vestingAmount","type":"uint256"},{"internalType":"address","name":"tokenAddr","type":"address"},{"internalType":"uint256","name":"tokenDeposited","type":"uint256"},{"internalType":"uint256","name":"tokenRemains","type":"uint256"},{"internalType":"uint8","name":"tokenDecimals","type":"uint8"},{"internalType":"uint256","name":"refundInvestors","type":"uint256"},{"internalType":"uint256","name":"refundDeadlineAt","type":"uint256"},{"internalType":"address","name":"refundTokenAddr","type":"address"},{"internalType":"uint256","name":"refundAmount","type":"uint256"},{"internalType":"uint8","name":"refundTokenDecimals","type":"uint8"},{"internalType":"uint256","name":"refundTokenDeposited","type":"uint256"},{"internalType":"uint256","name":"refundTokenRemains","type":"uint256"}],"internalType":"struct VestingProject","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTrustedMprSources","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceId_","type":"bytes32"}],"name":"isTrustedMprSource","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"projectIdCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_sourceId","type":"bytes32"}],"name":"removeTrustedMprSource","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_sourceChain","type":"string"},{"internalType":"string","name":"_sourceAddress","type":"string"}],"name":"removeTrustedMprSource","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId_","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"requestRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"s_claimedAmount","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"s_project","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"investors","type":"uint256"},{"internalType":"bytes32","name":"merkleProofRoot","type":"bytes32"},{"internalType":"uint256","name":"tgeAt","type":"uint256"},{"internalType":"uint256","name":"tgeAmount","type":"uint256"},{"internalType":"uint256","name":"cliffDuration","type":"uint256"},{"internalType":"uint256","name":"vestingDuration","type":"uint256"},{"internalType":"uint256","name":"vestingAmount","type":"uint256"},{"internalType":"address","name":"tokenAddr","type":"address"},{"internalType":"uint256","name":"tokenDeposited","type":"uint256"},{"internalType":"uint256","name":"tokenRemains","type":"uint256"},{"internalType":"uint8","name":"tokenDecimals","type":"uint8"},{"internalType":"uint256","name":"refundInvestors","type":"uint256"},{"internalType":"uint256","name":"refundDeadlineAt","type":"uint256"},{"internalType":"address","name":"refundTokenAddr","type":"address"},{"internalType":"uint256","name":"refundAmount","type":"uint256"},{"internalType":"uint8","name":"refundTokenDecimals","type":"uint8"},{"internalType":"uint256","name":"refundTokenDeposited","type":"uint256"},{"internalType":"uint256","name":"refundTokenRemains","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"s_refundRequestedAt","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"s_refundedAt","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId_","type":"uint256"},{"internalType":"bool","name":"status_","type":"bool"}],"name":"setProjectActiveStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId_","type":"uint256"},{"internalType":"uint256","name":"investors_","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot_","type":"bytes32"}],"name":"setProjectMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId_","type":"uint256"},{"internalType":"address","name":"refundTokenAddr_","type":"address"},{"internalType":"uint256","name":"refundAmount_","type":"uint256"},{"internalType":"uint256","name":"refundDeadlineAt_","type":"uint256"}],"name":"setRefundParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId_","type":"uint256"}],"name":"withdrawOrphanToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddr_","type":"address"}],"name":"withdrawUnusedToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

60c034620002495762004ac6906001600160401b03601f38849003908101601f1916830190828211848310176200024d57808491604096879485528339810103126200024957620000508262000261565b9062000060602080940162000261565b5f805460ff199081168255939092916001600160a01b039182168015620002385760805260019283600355875190610798808301918383109083111762000224579082916200432e8339039085f080156200021a57821660018060a01b0319600454161760045516938415620002065781907f79e553c6f53701daa99614646285e66adb98ff0fcc1ef165dd2718e5c873bee694858552828252878520338652825260ff888620541615620001bf575b5084845260028152620001263388862062000276565b508484525280858320018381549155837fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9181838680a47fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775918284528684200192848454945580a460a0525161402c9081620003028239608051818181611f6d015281816128af015261294e015260a05181611ca40152f35b858552828252878520338652825282888620918254161790553333867f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8780a45f62000110565b634e487b7160e01b83526004829052602483fd5b87513d86823e3d90fd5b634e487b7160e01b87526041600452602487fd5b875163e6c4247b60e01b8152600490fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036200024957565b9190600183015f90828252806020526040822054155f14620002fb5784549468010000000000000000861015620002e75760018601808255861015620002d357836040949596828552602085200155549382526020522055600190565b634e487b7160e01b83526032600452602483fd5b634e487b7160e01b83526041600452602483fd5b5092505056fe6101a0806040526004361015610013575f80fd5b5f3560e01c90816301ffc9a714612ca957508063021fbc2a14612a0a578063109b89581461297d578063116191b61461293957806314c85e831461290d5780631a98b2e0146127b85780631cd14f9e1461268e578063248a9ca3146126615780632e63ea021461247a5780632f2ff15d146123bd57806332f351191461237f57806336568abe146122ed5780633f4ba83a1461225c57806344423eb5146121365780634916065814611e9757806350f3baca14611e5357806356a9d20914611db85780635c975abb14611d975780635d0722dd14611d535780636225804e14611cd35780636a22d8cc14611c8f5780637059e37d14611ae357806372888ddf146116a857806374ba1824146114f157806375b238fc146114b75780637e8c7f081461147d5780638456cb59146114265780639010d07c146113e157806391d1485414611398578063a217fddf1461137e578063a7202e371461130c578063a77427151461105c578063aaba13ba14611018578063be3ce52714610f7d578063c13c09cc14610bea578063ca15c87314610bc0578063d547741f14610b81578063d5977b5e14610b64578063de7a7a5314610a87578063f0f3f2c8146107af5763fc1a7b78146101e0575f80fd5b346107ab5760e03660031901126107ab576004356001600160401b0381116107ab57610210903690600401612d5f565b60c435906024906001600160a01b03831683036107ab5761022f61301c565b64174876e800823510156107675760843515610723576001600160a01b038316156106df5760405163313ce56760e01b8152916020836004816001600160a01b0388165afa9283156106d4575f936106a3575b5060ff83161561065f576005549361029985613a79565b60055560ff604051946102ab86612dc4565b8686526102b936868a612f4d565b6020870152600160408701525f60608701525f6080870152833560a087015260443560c087015260643560e087015260843561010087015260a43561012087015260018060a01b0383166101408701525f6101608701525f610180870152166101a08501525f6101c08501525f6101e08501525f6102008501525f6102208501525f6102408501525f6102608501525f610280850152845f52600860205260405f208451815560208501518051906001600160401b03821161064c576103826001840154612d8c565b601f8111610608575b50918795939160209995938a90601f8311600114610570579882601493610280937f533305f07f74f15699dfc2890663a10beae506b24baab1bcf7b238947f47229f9b9c5f92610565575b50508160011b915f199060031b1c19161760018501555b61040c60408201511515600286019060ff801983541691151516179055565b606081015160038501556080810151600485015560a0810151600585015560c0810151600685015560e0810151600785015561010081015160088501556101208101516009850155600a840160018060a01b0361014083015116906bffffffffffffffffffffffff60a01b9182825416179055610160820151600b860155610180820151600c860155600d85019060ff6101a0840151169160ff1992838254161790556101c0830151600e8701556101e0830151600f870155601086019060018060a01b0361020085015116908254161790556102208201516011860155601285019060ff61024084015116908254161790556102608101516013850155015191015561052660405194859460e0865260e0860191612ffc565b91358389015260443560408401526064356060840152608435608084015260a43560a08401526001600160a01b031660c08301520390a2604051908152f35b015190505f806103d6565b6001849a93929a015f528b5f20905f5b601f19841681106105ee575082601494927f533305f07f74f15699dfc2890663a10beae506b24baab1bcf7b238947f47229f9b9c60019361028096601f198116106105d6575b505050811b0160018501556103ed565b01515f1960f88460031b161c191690555f80806105c6565b818c015183559a8d019a8c9a506001909201918d01610580565b600184015f5260205f20601f840160051c810160208510610645575b601f830160051c8201811061063a57505061038b565b5f8155600101610624565b5080610624565b84634e487b7160e01b5f5260416004525ffd5b60649060196040519162461bcd60e51b8352602060048401528201527f4173736572743a20746f6b656e446563696d616c73203e2030000000000000006044820152fd5b6106c691935060203d6020116106cd575b6106be8183612e2a565b810190613a60565b915f610282565b503d6106b4565b6040513d5f823e3d90fd5b60405162461bcd60e51b815260206004820152601a818401527f4173736572743a205f746f6b656e4164647220213d206e756c6c0000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601c818401527f4173736572743a205f76657374696e674475726174696f6e203e2030000000006044820152606490fd5b60405162461bcd60e51b8152602060048201526018818401527f353133382d31312d31365430393a34363a34302e3030305a00000000000000006044820152606490fd5b5f80fd5b346107ab5760203660031901126107ab575f6102806040516107d081612dc4565b828152606060208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e08201528261020082015282610220820152826102408201528261026082015201526004355f52600860205260405f2060146040519161087183612dc4565b8054835261088160018201612e4b565b602084015260ff600282015416151560408401526003810154606084015260048101546080840152600581015460a0840152600681015460c0840152600781015460e08401526008810154610100840152600981015461012084015260018060a01b03600a82015416610140840152600b810154610160840152600c81015461018084015260ff600d820154166101a0840152600e8101546101c0840152600f8101546101e084015260018060a01b03601082015416610200840152601181015461022084015260ff601282015416610240840152601381015461026084015201546102808201526040518091602082528051602083015261028061099760208301516102a060408601526102c0850190612f0d565b9160408101511515606085015260608101516080850152608081015160a085015260a081015160c085015260c081015160e085015260e081015161010085015261010081015161012085015261012081015161014085015260018060a01b03610140820151166101608501526101608101516101808501526101808101516101a085015260ff6101a0820151166101c08501526101c08101516101e08501526101e081015161020085015260018060a01b036102008201511661022085015261022081015161024085015260ff610240820151166102608501526102608101518285015201516102a08301520390f35b346107ab5760603660031901126107ab57600435602435604435610aa9613235565b5f838152600860205260409020600a0154610ace906001600160a01b03161515613a14565b825f52600860205260405f209160048301918254610b1f57806040938360037ff306a7e4b65014ba5d990b306e17bcc4fc7c87905af7048978eb564cab686b179701555582519182526020820152a2005b60405162461bcd60e51b815260206004820152601e60248201527f4d65726b6c652070726f6f6620726f6f7420686173206265656e2073657400006044820152606490fd5b346107ab575f3660031901126107ab576020600554604051908152f35b346107ab5760403660031901126107ab57610bbe600435610ba0612d16565b90805f526001602052610bb9600160405f200154613378565b61347f565b005b346107ab5760203660031901126107ab576004355f526002602052602060405f2054604051908152f35b346107ab5760403660031901126107ab5760246004356001600160401b0382358181116107ab57366023820112156107ab5780600401359182116107ab578160051b91368584840101116107ab57610c406139d1565b610c48613efc565b835f526020926008845260405f2091610d6f60405193610c6785612dc4565b80548552610c7760018201612e4b565b8786015260ff6002820154161515908160408701526003810154606087015260048101546080870152600581015460a0870152600681015460c0870152600781015460e08701526008810154610100870152600981015461012087015260018060a01b0395610280601488600a850154169384610140850152600b810154610160850152600c81015461018085015260ff600d820154166101a0850152600e8101546101c0850152600f8101546101e085015289601082015416610200850152601181015461022085015260ff6012820154166102408501526013810154610260850152015491015215159081610f75575b50613deb565b855f526008855260405f2093600485015492610d8c841515613e37565b846004541690610dae604051338a820152898152610da981612df4565b613e8e565b604051632c0b0d2160e11b8152600481019690965260608b87015260648601859052936001600160fb1b03106107ab57608485828a968296948e84960185850137604483015281010301915afa80156106d457610e12915f91610f48575b50613eb0565b835f526009835260405f20335f52835260405f2054610ef057610e353385613cbe565b97925050508515610eb75750847f4ec90e965519d92681267467f775ada5bd214aa92c0dc93d90a5e880ce9ed0269392610ea792865f52600b855260405f20335f52855260405f20610e8884825461352e565b9055600c8201610e9984825461379c565b9055600a3392015416613a87565b6040519384523393a36001600355005b8360136064926040519262461bcd60e51b84526004840152820152724e6f20636c61696d61626c6520746f6b656e7360681b6044820152fd5b60405162461bcd60e51b815260048101849052602b818701527f4e6f7420656c696769626c6520666f72206120636c61696d2028726566756e6460448201526a207265717565737465642960a81b6064820152608490fd5b610f689150853d8711610f6e575b610f608183612e2a565b810190612fe4565b87610e0c565b503d610f56565b905089610d69565b346107ab57610f8b36612fa1565b610f93613235565b6040516020810190610fb881610faa85878661397e565b03601f198101835282612e2a565b51902091610fd1835f52600760205260405f2054151590565b15610fd857005b611013836110067f48189fb0469e6320b26e21ec3d91baba52395b49f123e37f48bfec35843b0092956136b5565b50604051938493846139a3565b0390a1005b346107ab5760403660031901126107ab57611031612d16565b6004355f52600960205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b346107ab576020806003193601126107ab576004356110796139d1565b611081613efc565b805f526008825260405f206111a36040519161109c83612dc4565b805483526110ac60018201612e4b565b8584015260ff6002820154161515908160408501526003810154606085015260048101546080850152600581015460a0850152600681015460c0850152600781015460e08501526008810154610100850152600981015461012085015260018060a01b0393610280601486600a850154169384610140850152600b810154610160850152600c81015461018085015260ff600d820154166101a0850152600e8101546101c0850152600f8101546101e085015287601082015416610200850152601181015461022085015260ff60128201541661024085015260138101546102608501520154910152151590816113045750613deb565b815f526008835260405f20601181015491601082015416906111cb600f820154421015613f52565b835f526009855260405f20335f52855260405f2054156112af57835f52600a855260405f20335f52855260405f205461125e576014908495611231955f52600a815260405f2090335f52524260405f20550161122883825461379c565b90553390613a87565b33907f16e7a7a8fd7c51d6b4d059d6d7eea946bff398eee64dd1894af645edd23d502b5f80a36001600355005b60405162461bcd60e51b8152600481018690526024808201527f4e6f7420656c696769626c6520666f72206120726566756e642028726566756e6044820152636465642960e01b6064820152608490fd5b60405162461bcd60e51b815260048101869052602760248201527f4e6f7420656c696769626c6520666f72206120726566756e642028756e7265716044820152667565737465642960c81b6064820152608490fd5b905085610d69565b346107ab5761131a36612fa1565b611322613235565b604051602081019061133981610faa85878661397e565b51902091825f52600760205260405f205461135057005b611013836110067fa93d660afa144f75d00eb088e73864c981f08dd587df69df5de1feccbd476042956137a9565b346107ab575f3660031901126107ab5760206040515f8152f35b346107ab5760403660031901126107ab576113b1612d16565b6004355f52600160205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346107ab5760403660031901126107ab576004355f526002602052602061140d60243560405f206136a0565b905460405160039290921b1c6001600160a01b03168152f35b346107ab575f3660031901126107ab5761143e61301c565b6114466139d1565b600160ff195f5416175f557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346107ab575f3660031901126107ab5760206040517f79e553c6f53701daa99614646285e66adb98ff0fcc1ef165dd2718e5c873bee68152f35b346107ab575f3660031901126107ab5760206040517fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217758152f35b346107ab576020806003193601126107ab5760043561150e613235565b5f8181526008835260409020600a01546001600160a01b03906115349082161515613a14565b815f526008835260405f20600681015490600981015492600a8201541692600b820192835491600f840154421061164f5760038401549161158f600e8601549361157f851515613c72565b611589848461352e565b90613507565b840361161257937f24e88bf6d6cc3807a0c63437666521583d595cca6bc2bc909506961d54cb10b296936115d0608097946115896115e695600c9c9961352e565b988992016115df83825461379c565b905561379c565b90556115f3853383613a87565b60405191825285820152836040820152336060820152a1604051908152f35b60405162461bcd60e51b8152600481018a90526015602482015274105cdcd95c9d0e88085bdc9c1a185b905b5bdd5b9d605a1b6044820152606490fd5b60405162461bcd60e51b815260048101899052602b60248201527f4173736572743a20626c6f636b2e74696d657374616d70203e3d20726566756e60448201526a19111958591b1a5b99505d60aa1b6064820152608490fd5b346107ab5760403660031901126107ab57600435602480356001600160401b038082116107ab57366023830112156107ab578160040135908111611ad0578060051b91602092604051926116fe85830185612e2a565b83528484840191830101913683116107ab5785859101915b838310611ac057505050506117296139d1565b611731613efc565b835f526008825260405f20916118546040519361174d85612dc4565b8054855261175d60018201612e4b565b8386015260ff6002820154161515908160408701526003810154606087015260048101546080870152600581015460a0870152600681015460c0870152600781015460e08701526008810154610100870152600981015461012087015260018060a01b0395610280601488600a850154169384610140850152600b810154610160850152600c81015461018085015260ff600d820154166101a0850152600e8101546101c0850152600f8101546101e085015289601082015416610200850152601181015461022085015260ff6012820154166102408501526013810154610260850152015491015215159081611ab85750613deb565b845f526008815260405f209160048301549380601085015416611878861515613e37565b15611a7457600f840154421015611a3057865f526009835260405f20335f52835260405f20546119f957865f52600b835260405f20335f52835260405f20546119a95782916119079160045416906118dd6040513386820152858152610da981612df4565b9660405197889485938493632c0b0d2160e11b8552600485015260608c8501526064840190612d2c565b90604483015203915afa9283156106d457600e9361192b915f916119925750613eb0565b845f526009815260405f2090335f52524260405f2055018054916001830180931161197f57505533907fd8420f97dab0b820e066ebfad617848c1eab2d260922358cc1d5cdcdc568d5725f80a36001600355005b634e487b7160e01b5f9081526011600452fd5b610f689150833d8511610f6e57610f608183612e2a565b60405162461bcd60e51b8152600481018490526023818801527f4e6f7420656c696769626c6520666f72206120726566756e642028636c61696d60448201526265642960e81b6064820152608490fd5b60405162461bcd60e51b8152600481018490526010818801526f1499599d5b99081c995c5d595cdd195960821b6044820152606490fd5b60405162461bcd60e51b815260048101849052601a818801527f526566756e6420646561646c696e6520686173207061737365640000000000006044820152606490fd5b60405162461bcd60e51b815260048101849052601e818801527f526566756e6420706172616d7320686173206e6f74206265656e2073657400006044820152606490fd5b905087610d69565b8235815291810191859101611716565b82634e487b7160e01b5f5260416004525ffd5b346107ab5760203660031901126107ab576004355f52600860205260405f2080549060018101611b1290612e4b565b61014052600281015460ff1661012052600381015461016052600481015461018052600581015491600682015490600783015491600884015492600985015493600160a01b600190039485600a8801541690600b88015492600c89015494600d8a015460ff1696600e8b015498600f8c01549a60108d0154169b6011810154608052601281015460ff1660a052601381015460e052601401546101005260405160c0526102a09060c051528060c0516020015260c051016101405190611bd791612f0d565b9b61012051151560c051604001526101605160c051606001526101805160c0516080015260c05160a0015260c05160c0015260c05160e0015260c051610100015260c051610120015260c051610140015260c051610160015260c051610180015260c0516101a0015260c0516101c0015260c0516101e0015260c051610200015260805160c051610220015260a05160c051610240015260e05160c05161026001526101005160c051610280015260c051900360c051f35b346107ab575f3660031901126107ab576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346107ab5760203660031901126107ab57600435611cef613235565b805f52600760205260405f2054611d0257005b60a081611d2f7fa93d660afa144f75d00eb088e73864c981f08dd587df69df5de1feccbd476042936137a9565b5060405190606082525f6060830152608060208301525f60808301526040820152a1005b346107ab5760403660031901126107ab57611d6c612d16565b6004355f52600a60205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b346107ab575f3660031901126107ab57602060ff5f54166040519015158152f35b346107ab5760403660031901126107ab57600435602435801515908181036107ab577fd751c6369b67c9aaa0c63a4550e51685b2d4e123d1a30c9b84b97e4a5fa64d2391611e4a602092855f5260088452611e2360018060a01b03600a60405f200154161515613a14565b611e2b61301c565b855f5260088452600260405f20019060ff801983541691151516179055565b604051908152a2005b346107ab5760403660031901126107ab57611e6c612d16565b6004355f52600b60205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b346107ab576003196080368201126107ab576001600160401b03906024358281116107ab57611eca903690600401612d5f565b6044929192358481116107ab57611ee5903690600401612d5f565b946064359081116107ab57611efe903690600401612d5f565b929094611f0c368588612f4d565b96875160208099012088611f6160018060a01b03986040519384928392635f6970c360e01b8452600435600485015260806024850152611f50608485018b8b612ffc565b90848203016044850152878b612ffc565b90606483015203815f8b7f0000000000000000000000000000000000000000000000000000000000000000165af19081156106d4575f91612119575b501561210757611ffe93611fca92611fde611fea936040519586938d85019860408a526060860191612ffc565b90601f199485858403016040860152612ffc565b03908101835282612e2a565b5190205f52600760205260405f2054151590565b156120cb5782606091810103126107ab5781359261203660408285013594013592855f5260088352600a60405f200154161515613a14565b835f526008815260405f2060048101908154612086579183604094928660037ff306a7e4b65014ba5d990b306e17bcc4fc7c87905af7048978eb564cab686b1798960155558351928352820152a2005b60405162461bcd60e51b815260048101849052601e60248201527f4d65726b6c652070726f6f6620726f6f7420686173206265656e2073657400006044820152606490fd5b60405162461bcd60e51b8152600481018590526014602482015273556e74727573746564206d707220736f7572636560601b6044820152606490fd5b604051631403112d60e21b8152600490fd5b6121309150893d8b11610f6e57610f608183612e2a565b89611f9d565b346107ab5760203660031901126107ab576004355f818152600860205260409020600a01546001600160a01b03906121719082161515613a14565b815f52600860205260405f20600e810154916011820154906010830154169260138301928354612218577feb1b7060d355a76dcaf4f04914a551bc4a9480aebf2e5ff33caeb897c7dad29594612213946121eb6121fd946014966121db600f870154421015613f52565b6121e6821515613c72565b613507565b94859384809355015530903390613f9e565b6040805191825233602083015290918291820190565b0390a2005b606460405162461bcd60e51b815260206004820152602060248201527f4173736572743a20726566756e64546f6b656e4465706f7369746564203d20306044820152fd5b346107ab575f3660031901126107ab5761227461301c565b5f5460ff8116156122b15760ff19165f557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606490fd5b346107ab5760403660031901126107ab57612306612d16565b336001600160a01b0382160361232257610bbe9060043561347f565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b346107ab5760403660031901126107ab5760806123a561239d612d16565b600435613cbe565b91604051938452602084015260408301526060820152f35b346107ab5760403660031901126107ab57610bbe60043560026123de612d16565b91805f52602090600182526123f9600160405f200154613378565b5f8181526001835260408082206001600160a01b0390961680835295845290205460ff161561242f575b5f525260405f20613730565b805f526001825260405f20845f52825260405f20600160ff198254161790553384827f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4612423565b346107ab576020806003193601126107ab576001600160a01b03600435818116908190036107ab576124aa613235565b801561261c576040516370a0823160e01b81523060048201528381602481855afa9081156106d4575f916125ef575b5091600554905f5b82811061257a575050508115612535576124fc823383613a87565b604080518381523360208201527f12b2eecb5e61422ef43355aec5faab70327c566a09f5ccb0ff5eca75a5ad40939190a2604051908152f35b60405162461bcd60e51b815260048101849052601960248201527f4173736572743a20756e7573656442616c616e6365203e2030000000000000006044820152606490fd5b805f52600880875260405f208584600a83015416146125d45750815f52865260405f208483601083015416146125ba575b506125b590613a79565b6124e1565b6125b5919560146125cd9201549061379c565b94906125ab565b6125e9915095600c6125b5939701549061379c565b94613a79565b90508381813d8311612615575b6126068183612e2a565b810103126107ab5751846124d9565b503d6125fc565b60405162461bcd60e51b815260048101849052601a60248201527f4173736572743a20746f6b656e416464725f20213d206e756c6c0000000000006044820152606490fd5b346107ab5760203660031901126107ab576004355f5260016020526020600160405f200154604051908152f35b346107ab5760203660031901126107ab576004355f818152600860205260409020600a01546001600160a01b03906126c99082161515613a14565b815f52600860205260405f209060038201546006830154600984015492600a8501541693600b8101948554612773578315612736577f754b20cbde6413b3561d41cba275e6331494efb5b031b17641440f14b7755e48956121eb600c956121e6612213986121fd9761352e565b60405162461bcd60e51b815260206004820152601560248201527404173736572743a20696e766573746f7273203e203605c1b6044820152606490fd5b60405162461bcd60e51b815260206004820152601a60248201527f4173736572743a20746f6b656e4465706f7369746564203d20300000000000006044820152606490fd5b346107ab5760031960c0368201126107ab576001600160401b036024358181116107ab576127ea903690600401612d5f565b6044358381116107ab57612802903690600401612d5f565b6064358581116107ab5761281a903690600401612d5f565b6084359687116107ab5761287e9761288e6128a29561284c61284260209b3690600401612d5f565b9690953691612f4d565b8a8151910120956040519b8c9a8b9a631876eed960e01b8c5260043560048d015260c060248d015260c48c0191612ffc565b91848a84030160448b0152612ffc565b936064870152858403016084860152612ffc565b60a480359083015203815f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af19081156106d4575f916128ef575b501561210757005b612907915060203d8111610f6e57610f608183612e2a565b816128e7565b346107ab5760203660031901126107ab576004355f526007602052602060405f20541515604051908152f35b346107ab575f3660031901126107ab576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346107ab575f3660031901126107ab576040518060065480825282602080930160065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f925f5b858282106129f4575050506129dc92500383612e2a565b6129f0604051928284938452830190612d2c565b0390f35b85548452600195860195889550930192016129c5565b346107ab5760803660031901126107ab57600435612a26612d16565b604490813590606491823591855f526020906008825260018060a01b0394612a5886600a60405f200154161515613a14565b612a6061301c565b875f526008835260405f20916010830191825491888316612c675760405163313ce56760e01b8152908916999086816004818e5afa80156106d45760ff915f91612c4a575b5016988915612c08578715612bc6578815612b8457600a860154168a14612b36578a7fd57b76b8142bba6badaf6950d98fab6fd3a7cc559e1f5e8f965864746d7fe18360808c8c8c8c8c60128d8d878e86601185015587600f8501556bffffffffffffffffffffffff60a01b16179055018460ff1982541617905560405194855284015260408301526060820152a2005b6320b2323960e11b6084927f4173736572743a20726566756e64546f6b656e4164647220213d20746f6b656e88936040519462461bcd60e51b86526004860152602480860152840152820152fd5b507f4173736572743a20726566756e64446561646c696e6541745f203e203000000086916040519262461bcd60e51b84526004840152601d6024840152820152fd5b507f4173736572743a20726566756e64416d6f756e745f203e20300000000000000086916040519262461bcd60e51b8452600484015260196024840152820152fd5b507f4173736572743a20726566756e64546f6b656e446563696d616c73203e20300086916040519262461bcd60e51b84526004840152601f6024840152820152fd5b612c619150883d8a116106cd576106be8183612e2a565b8d612aa5565b5060405162461bcd60e51b815260048101869052601a60248201527f526566756e6420706172616d7320686173206265656e20736574000000000000818b0152fd5b346107ab5760203660031901126107ab576004359063ffffffff60e01b82168092036107ab57602091635a05180f60e01b8114908115612ceb575b5015158152f35b637965db0b60e01b811491508115612d05575b5083612ce4565b6301ffc9a760e01b14905083612cfe565b602435906001600160a01b03821682036107ab57565b9081518082526020808093019301915f5b828110612d4b575050505090565b835185529381019392810192600101612d3d565b9181601f840112156107ab578235916001600160401b0383116107ab57602083818601950101116107ab57565b90600182811c92168015612dba575b6020831014612da657565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612d9b565b6102a081019081106001600160401b03821117612de057604052565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117612de057604052565b608081019081106001600160401b03821117612de057604052565b90601f801991011681019081106001600160401b03821117612de057604052565b9060405191825f8254612e5d81612d8c565b9081845260209460019182811690815f14612eca5750600114612e8c575b505050612e8a92500383612e2a565b565b5f90815285812095935091905b818310612eb2575050612e8a93508201015f8080612e7b565b85548884018501529485019487945091830191612e99565b92505050612e8a94925060ff191682840152151560051b8201015f8080612e7b565b5f5b838110612efd5750505f910152565b8181015183820152602001612eee565b90602091612f2681518092818552858086019101612eec565b601f01601f1916010190565b6001600160401b038111612de057601f01601f191660200190565b929192612f5982612f32565b91612f676040519384612e2a565b8294818452818301116107ab578281602093845f960137010152565b9080601f830112156107ab57816020612f9e93359101612f4d565b90565b9060406003198301126107ab576001600160401b036004358181116107ab5783612fcd91600401612f83565b926024359182116107ab57612f9e91600401612f83565b908160209103126107ab575180151581036107ab5790565b908060209392818452848401375f828201840152601f01601f1916010190565b335f9081527f50efbde2d46c37e9785f1791697f77e94bb7b701e19f1930a668820722d37694602090815260408083205490927fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217759160019060ff1615613083575050505050565b61308c33613560565b9285519161309983612e0f565b60428352858301936060368637835115613221576030855383518210156132215790607860218501536041915b8183116131b45750505061317257604861316e93869361315293613143985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a86015261311a815180928c603789019101612eec565b8401917001034b99036b4b9b9b4b733903937b6329607d1b603784015251809386840190612eec565b01036028810187520185612e2a565b5192839262461bcd60e51b845260048401526024830190612f0d565b0390fd5b60648486519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b909192600f8116601081101561320d576f181899199a1a9b1b9c1cb0b131b232b360811b901a6131e4858761353b565b5360041c9280156131f9575f190191906130c6565b634e487b7160e01b82526011600452602482fd5b634e487b7160e01b83526032600452602483fd5b634e487b7160e01b81526032600452602490fd5b335f9081527fd3ee5543a19ed9f63e85698aded3c4654a61a2e3d0a0400d95a23f1e05193136602090815260408083205490927f79e553c6f53701daa99614646285e66adb98ff0fcc1ef165dd2718e5c873bee69160019060ff161561329c575050505050565b6132a533613560565b928551916132b283612e0f565b60428352858301936060368637835115613221576030855383518210156132215790607860218501536041915b8183116133335750505061317257604861316e93869361315293613143985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a86015261311a815180928c603789019101612eec565b909192600f8116601081101561320d576f181899199a1a9b1b9c1cb0b131b232b360811b901a613363858761353b565b5360041c9280156131f9575f190191906132df565b5f818152600191602091838352604093848220338352845260ff8583205416156133a3575050505050565b6133ac33613560565b928551916133b983612e0f565b60428352858301936060368637835115613221576030855383518210156132215790607860218501536041915b81831161343a5750505061317257604861316e93869361315293613143985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a86015261311a815180928c603789019101612eec565b909192600f8116601081101561320d576f181899199a1a9b1b9c1cb0b131b232b360811b901a61346a858761353b565b5360041c9280156131f9575f190191906133e6565b9060406134bc925f9080825260016020528282209360018060a01b03169384835260205260ff83832054166134bf575b8152600260205220613891565b50565b808252600160205282822084835260205282822060ff1981541690553384827ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b8580a46134af565b8181029291811591840414171561351a57565b634e487b7160e01b5f52601160045260245ffd5b9190820180921161351a57565b90815181101561354c570160200190565b634e487b7160e01b5f52603260045260245ffd5b60405190606082018281106001600160401b03821117612de057604052602a825260208201604036823782511561354c5760309053815160019081101561354c57607860218401536029905b8082116136005750506135bc5790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f81166010811015613657576f181899199a1a9b1b9c1cb0b131b232b360811b901a61362f848661353b565b5360041c918015613643575f1901906135ac565b60245f634e487b7160e01b81526011600452fd5b60245f634e487b7160e01b81526032600452fd5b60065481101561354c5760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01905f90565b805482101561354c575f5260205f2001905f90565b5f8181526007602052604081205461372b57600654600160401b8110156137175790826137036136ed8460016040960160065561366b565b819391549060031b91821b915f19901b19161790565b905560065492815260076020522055600190565b634e487b7160e01b82526041600452602482fd5b905090565b9190600183015f90828252806020526040822054155f1461379657845494600160401b86101561378257836137726136ed886001604098999a018555846136a0565b9055549382526020522055600190565b634e487b7160e01b83526041600452602483fd5b50925050565b9190820391821161351a57565b5f81815260076020526040812054909190801561388c575f1990808201818111613878576006549083820191821161386457808203613830575b505050600654801561381c578101906137fb8261366b565b909182549160031b1b19169055600655815260076020526040812055600190565b634e487b7160e01b84526031600452602484fd5b61384e61383f6136ed9361366b565b90549060031b1c92839261366b565b90558452600760205260408420555f80806137e3565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b85526011600452602485fd5b505090565b9060018201905f92818452826020526040842054908115155f14613977575f19918083018181116139635782549084820191821161394f5780820361391a575b50505080548015613906578201916138e983836136a0565b909182549160031b1b191690555582526020526040812055600190565b634e487b7160e01b86526031600452602486fd5b61393a61392a6136ed93866136a0565b90549060031b1c928392866136a0565b905586528460205260408620555f80806138d1565b634e487b7160e01b88526011600452602488fd5b634e487b7160e01b87526011600452602487fd5b5050505090565b9091613995612f9e93604084526040840190612f0d565b916020818403910152612f0d565b9392916139cc906139be604093606088526060880190612f0d565b908682036020880152612f0d565b930152565b60ff5f54166139dc57565b60405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606490fd5b15613a1b57565b60405162461bcd60e51b815260206004820152601a60248201527f5468652050726f6a65637420646f6573206e6f742065786973740000000000006044820152606490fd5b908160209103126107ab575160ff811681036107ab5790565b5f19811461351a5760010190565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815260808101916001600160401b03831182841017612de057612e8a926040525b604051613b4d916001600160a01b0316613aeb82612df4565b5f806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af13d15613bd5573d91613b3283612f32565b92613b406040519485612e2a565b83523d5f8785013e613bd9565b805190828215928315613bbd575b50505015613b665750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b613bcd9350820181019101612fe4565b5f8281613b5b565b6060915b91929015613c3b5750815115613bed575090565b3b15613bf65790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015613c4e5750805190602001fd5b60405162461bcd60e51b81526020600482015290819061316e906024830190612f0d565b15613c7957565b60405162461bcd60e51b815260206004820152601b60248201527f4173736572743a20726566756e64496e766573746f7273203e203000000000006044820152606490fd5b90915f908282526020916009835260408082209560018060a01b031695868352845260088183205494868452600a815282842088855281528284205497878552600b825283852090855281528284205496845252812084600582015460068301549060098401549360086007820154910154918042105f14613d4857505050505050915b93929190565b613d58828298949596979861352e565b4211613d725750505050613d6c925061379c565b91613d42565b613d939293949550613d8d91613d879161352e565b4261379c565b85613507565b908215613dd7575091613dad613d6c94928894048261352e565b91613db8828261352e565b8311613dc6575b505061379c565b613dd0925061352e565b5f80613dbf565b634e487b7160e01b81526012600452602490fd5b15613df257565b60405162461bcd60e51b815260206004820152601b60248201527f5468652050726f6a65637420646f6573206e6f742061637469766500000000006044820152606490fd5b15613e3e57565b60405162461bcd60e51b815260206004820152602260248201527f4d65726b6c652070726f6f6620726f6f7420686173206e6f74206265656e2073604482015261195d60f21b6064820152608490fd5b602081519101519060208110613ea2575090565b5f199060200360031b1b1690565b15613eb757565b60405162461bcd60e51b815260206004820152601960248201527f4d69736d6174636820696e766573746d656e742070726f6f66000000000000006044820152606490fd5b600260035414613f0d576002600355565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b15613f5957565b60405162461bcd60e51b815260206004820152601e60248201527f526566756e6420646561646c696e6520686173206e6f742070617373656400006044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815260a08101918183106001600160401b03841117612de057612e8a92604052613ad256fea2646970667358221220a90295ff94c3a212f40d4e0a6a4f0f7f6606a0e398d22a09e21127212dec433864736f6c63430008150033608080604052346100165761077d908161001b8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f803560e01c9081632e08d602146100775750806358161a4214610072578063b86161951461006d578063cf06c7b714610068578063dd1d2599146100635763e27d51531461005e575f80fd5b6103ea565b6103c9565b6103a3565b6102b1565b610205565b3461014e57602036600319011261014e5760043567ffffffffffffffff811161014a576100a89036906004016101a8565b600180825111156100f957805b6100db575b6100d76100c68361044a565b516040519081529081906020820190565b0390f35b80825111156100f4576100ee81926104ff565b916100b5565b6100ba565b60405162461bcd60e51b815260206004820152602360248201527f776f6e27742067656e657261746520726f6f7420666f722073696e676c65206c60448201526232b0b360e91b6064820152608490fd5b5080fd5b80fd5b634e487b7160e01b5f52604160045260245ffd5b6040519190601f01601f1916820167ffffffffffffffff81118382101761018b57604052565b610151565b67ffffffffffffffff811161018b5760051b60200190565b9080601f830112156102015760209082356101ca6101c582610190565b610165565b93838086848152019260051b820101928311610201578301905b8282106101f2575050505090565b813581529083019083016101e4565b5f80fd5b346102015760603660031901126102015760243567ffffffffffffffff8111610201576102369036906004016101a8565b60443581515f925b8184106102545760405160043584148152602090f35b909161026d600191610266868561046b565b519061042a565b930192919061023e565b602090816040818301928281528551809452019301915f5b82811061029d575050505090565b83518552938101939281019260010161028f565b346102015760403660031901126102015760043567ffffffffffffffff8111610201576102e29036906004016101a8565b60243590806103076103026001936102fc8582511161047f565b51610618565b6104d7565b925f80845b61031f575b604051806100d78882610277565b83518581111561039d5785849392610362928280809816145f1461036c575061034b5f1986018861046b565b51610356828b61046b565b525b0192841c946104ff565b939291909161030c565b858301908103610388575083610382828b61046b565b52610358565b610392908861046b565b51610382828b61046b565b50610311565b346102015760203660031901126102015760206103c1600435610618565b604051908152f35b346102015760403660031901126102015760206103c160243560043561042a565b346102015760203660031901126102015760043560015f82805b61041c57602093508060405193811901161490038152f35b8293831c9384920191610404565b8181101561043f575f526020525b60405f2090565b905f52602052610438565b8051156104575760200190565b634e487b7160e01b5f52603260045260245ffd5b80518210156104575760209160051b010190565b1561048657565b60405162461bcd60e51b8152602060048201526024808201527f776f6e27742067656e65726174652070726f6f6620666f722073696e676c65206044820152633632b0b360e11b6064820152608490fd5b906104e46101c583610190565b82815280926104f5601f1991610190565b0190602036910137565b90815160019081808216145f146105915761051d8282811c016104d7565b915f1961052c8382018761046b565b515f805260205261054460405f20918551018561046b565b525b5f19909101905f805b83821061055f5750929450505050565b8260029161057d610570858b61046b565b516102668487018c61046b565b610587828961046b565b520191019061054f565b61059c81831c6104d7565b91610546565b90604082018092116105b057565b634e487b7160e01b5f52601160045260245ffd5b90602082018092116105b057565b90601082018092116105b057565b90600882018092116105b057565b90600482018092116105b057565b90600282018092116105b057565b90600182018092116105b057565b6001811115610742575f9080600160801b811015610737575b80680100000000000000006002921015610724575b640100000000811015610711575b620100008110156106fe575b6101008110156106eb575b60108110156106d8575b60048110156106c6575b10156106b6575b80610691811961060a565b1614806106ad575b156106a15790565b6106aa9061060a565b90565b50801515610699565b906106c09061060a565b90610686565b6106d290821c936105fc565b9261067f565b6106e59060041c936105ee565b92610675565b6106f89060081c936105e0565b9261066b565b61070b9060101c936105d2565b92610660565b61071e9060201c936105c4565b92610654565b6107319060401c936105a2565b92610646565b60809250821c610631565b505f9056fea26469706673582212209818c0d5d37362e3b493f593ae10f1f12620f626d428cbf656ccb44cad9c3b9464736f6c634300081500330000000000000000000000004f4495243837681061c4743b74b3eedf548d56a50000000000000000000000002d5d7d31f671f86c782533cc367f14109a082712

Deployed Bytecode

0x6101a0806040526004361015610013575f80fd5b5f3560e01c90816301ffc9a714612ca957508063021fbc2a14612a0a578063109b89581461297d578063116191b61461293957806314c85e831461290d5780631a98b2e0146127b85780631cd14f9e1461268e578063248a9ca3146126615780632e63ea021461247a5780632f2ff15d146123bd57806332f351191461237f57806336568abe146122ed5780633f4ba83a1461225c57806344423eb5146121365780634916065814611e9757806350f3baca14611e5357806356a9d20914611db85780635c975abb14611d975780635d0722dd14611d535780636225804e14611cd35780636a22d8cc14611c8f5780637059e37d14611ae357806372888ddf146116a857806374ba1824146114f157806375b238fc146114b75780637e8c7f081461147d5780638456cb59146114265780639010d07c146113e157806391d1485414611398578063a217fddf1461137e578063a7202e371461130c578063a77427151461105c578063aaba13ba14611018578063be3ce52714610f7d578063c13c09cc14610bea578063ca15c87314610bc0578063d547741f14610b81578063d5977b5e14610b64578063de7a7a5314610a87578063f0f3f2c8146107af5763fc1a7b78146101e0575f80fd5b346107ab5760e03660031901126107ab576004356001600160401b0381116107ab57610210903690600401612d5f565b60c435906024906001600160a01b03831683036107ab5761022f61301c565b64174876e800823510156107675760843515610723576001600160a01b038316156106df5760405163313ce56760e01b8152916020836004816001600160a01b0388165afa9283156106d4575f936106a3575b5060ff83161561065f576005549361029985613a79565b60055560ff604051946102ab86612dc4565b8686526102b936868a612f4d565b6020870152600160408701525f60608701525f6080870152833560a087015260443560c087015260643560e087015260843561010087015260a43561012087015260018060a01b0383166101408701525f6101608701525f610180870152166101a08501525f6101c08501525f6101e08501525f6102008501525f6102208501525f6102408501525f6102608501525f610280850152845f52600860205260405f208451815560208501518051906001600160401b03821161064c576103826001840154612d8c565b601f8111610608575b50918795939160209995938a90601f8311600114610570579882601493610280937f533305f07f74f15699dfc2890663a10beae506b24baab1bcf7b238947f47229f9b9c5f92610565575b50508160011b915f199060031b1c19161760018501555b61040c60408201511515600286019060ff801983541691151516179055565b606081015160038501556080810151600485015560a0810151600585015560c0810151600685015560e0810151600785015561010081015160088501556101208101516009850155600a840160018060a01b0361014083015116906bffffffffffffffffffffffff60a01b9182825416179055610160820151600b860155610180820151600c860155600d85019060ff6101a0840151169160ff1992838254161790556101c0830151600e8701556101e0830151600f870155601086019060018060a01b0361020085015116908254161790556102208201516011860155601285019060ff61024084015116908254161790556102608101516013850155015191015561052660405194859460e0865260e0860191612ffc565b91358389015260443560408401526064356060840152608435608084015260a43560a08401526001600160a01b031660c08301520390a2604051908152f35b015190505f806103d6565b6001849a93929a015f528b5f20905f5b601f19841681106105ee575082601494927f533305f07f74f15699dfc2890663a10beae506b24baab1bcf7b238947f47229f9b9c60019361028096601f198116106105d6575b505050811b0160018501556103ed565b01515f1960f88460031b161c191690555f80806105c6565b818c015183559a8d019a8c9a506001909201918d01610580565b600184015f5260205f20601f840160051c810160208510610645575b601f830160051c8201811061063a57505061038b565b5f8155600101610624565b5080610624565b84634e487b7160e01b5f5260416004525ffd5b60649060196040519162461bcd60e51b8352602060048401528201527f4173736572743a20746f6b656e446563696d616c73203e2030000000000000006044820152fd5b6106c691935060203d6020116106cd575b6106be8183612e2a565b810190613a60565b915f610282565b503d6106b4565b6040513d5f823e3d90fd5b60405162461bcd60e51b815260206004820152601a818401527f4173736572743a205f746f6b656e4164647220213d206e756c6c0000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601c818401527f4173736572743a205f76657374696e674475726174696f6e203e2030000000006044820152606490fd5b60405162461bcd60e51b8152602060048201526018818401527f353133382d31312d31365430393a34363a34302e3030305a00000000000000006044820152606490fd5b5f80fd5b346107ab5760203660031901126107ab575f6102806040516107d081612dc4565b828152606060208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e08201528261020082015282610220820152826102408201528261026082015201526004355f52600860205260405f2060146040519161087183612dc4565b8054835261088160018201612e4b565b602084015260ff600282015416151560408401526003810154606084015260048101546080840152600581015460a0840152600681015460c0840152600781015460e08401526008810154610100840152600981015461012084015260018060a01b03600a82015416610140840152600b810154610160840152600c81015461018084015260ff600d820154166101a0840152600e8101546101c0840152600f8101546101e084015260018060a01b03601082015416610200840152601181015461022084015260ff601282015416610240840152601381015461026084015201546102808201526040518091602082528051602083015261028061099760208301516102a060408601526102c0850190612f0d565b9160408101511515606085015260608101516080850152608081015160a085015260a081015160c085015260c081015160e085015260e081015161010085015261010081015161012085015261012081015161014085015260018060a01b03610140820151166101608501526101608101516101808501526101808101516101a085015260ff6101a0820151166101c08501526101c08101516101e08501526101e081015161020085015260018060a01b036102008201511661022085015261022081015161024085015260ff610240820151166102608501526102608101518285015201516102a08301520390f35b346107ab5760603660031901126107ab57600435602435604435610aa9613235565b5f838152600860205260409020600a0154610ace906001600160a01b03161515613a14565b825f52600860205260405f209160048301918254610b1f57806040938360037ff306a7e4b65014ba5d990b306e17bcc4fc7c87905af7048978eb564cab686b179701555582519182526020820152a2005b60405162461bcd60e51b815260206004820152601e60248201527f4d65726b6c652070726f6f6620726f6f7420686173206265656e2073657400006044820152606490fd5b346107ab575f3660031901126107ab576020600554604051908152f35b346107ab5760403660031901126107ab57610bbe600435610ba0612d16565b90805f526001602052610bb9600160405f200154613378565b61347f565b005b346107ab5760203660031901126107ab576004355f526002602052602060405f2054604051908152f35b346107ab5760403660031901126107ab5760246004356001600160401b0382358181116107ab57366023820112156107ab5780600401359182116107ab578160051b91368584840101116107ab57610c406139d1565b610c48613efc565b835f526020926008845260405f2091610d6f60405193610c6785612dc4565b80548552610c7760018201612e4b565b8786015260ff6002820154161515908160408701526003810154606087015260048101546080870152600581015460a0870152600681015460c0870152600781015460e08701526008810154610100870152600981015461012087015260018060a01b0395610280601488600a850154169384610140850152600b810154610160850152600c81015461018085015260ff600d820154166101a0850152600e8101546101c0850152600f8101546101e085015289601082015416610200850152601181015461022085015260ff6012820154166102408501526013810154610260850152015491015215159081610f75575b50613deb565b855f526008855260405f2093600485015492610d8c841515613e37565b846004541690610dae604051338a820152898152610da981612df4565b613e8e565b604051632c0b0d2160e11b8152600481019690965260608b87015260648601859052936001600160fb1b03106107ab57608485828a968296948e84960185850137604483015281010301915afa80156106d457610e12915f91610f48575b50613eb0565b835f526009835260405f20335f52835260405f2054610ef057610e353385613cbe565b97925050508515610eb75750847f4ec90e965519d92681267467f775ada5bd214aa92c0dc93d90a5e880ce9ed0269392610ea792865f52600b855260405f20335f52855260405f20610e8884825461352e565b9055600c8201610e9984825461379c565b9055600a3392015416613a87565b6040519384523393a36001600355005b8360136064926040519262461bcd60e51b84526004840152820152724e6f20636c61696d61626c6520746f6b656e7360681b6044820152fd5b60405162461bcd60e51b815260048101849052602b818701527f4e6f7420656c696769626c6520666f72206120636c61696d2028726566756e6460448201526a207265717565737465642960a81b6064820152608490fd5b610f689150853d8711610f6e575b610f608183612e2a565b810190612fe4565b87610e0c565b503d610f56565b905089610d69565b346107ab57610f8b36612fa1565b610f93613235565b6040516020810190610fb881610faa85878661397e565b03601f198101835282612e2a565b51902091610fd1835f52600760205260405f2054151590565b15610fd857005b611013836110067f48189fb0469e6320b26e21ec3d91baba52395b49f123e37f48bfec35843b0092956136b5565b50604051938493846139a3565b0390a1005b346107ab5760403660031901126107ab57611031612d16565b6004355f52600960205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b346107ab576020806003193601126107ab576004356110796139d1565b611081613efc565b805f526008825260405f206111a36040519161109c83612dc4565b805483526110ac60018201612e4b565b8584015260ff6002820154161515908160408501526003810154606085015260048101546080850152600581015460a0850152600681015460c0850152600781015460e08501526008810154610100850152600981015461012085015260018060a01b0393610280601486600a850154169384610140850152600b810154610160850152600c81015461018085015260ff600d820154166101a0850152600e8101546101c0850152600f8101546101e085015287601082015416610200850152601181015461022085015260ff60128201541661024085015260138101546102608501520154910152151590816113045750613deb565b815f526008835260405f20601181015491601082015416906111cb600f820154421015613f52565b835f526009855260405f20335f52855260405f2054156112af57835f52600a855260405f20335f52855260405f205461125e576014908495611231955f52600a815260405f2090335f52524260405f20550161122883825461379c565b90553390613a87565b33907f16e7a7a8fd7c51d6b4d059d6d7eea946bff398eee64dd1894af645edd23d502b5f80a36001600355005b60405162461bcd60e51b8152600481018690526024808201527f4e6f7420656c696769626c6520666f72206120726566756e642028726566756e6044820152636465642960e01b6064820152608490fd5b60405162461bcd60e51b815260048101869052602760248201527f4e6f7420656c696769626c6520666f72206120726566756e642028756e7265716044820152667565737465642960c81b6064820152608490fd5b905085610d69565b346107ab5761131a36612fa1565b611322613235565b604051602081019061133981610faa85878661397e565b51902091825f52600760205260405f205461135057005b611013836110067fa93d660afa144f75d00eb088e73864c981f08dd587df69df5de1feccbd476042956137a9565b346107ab575f3660031901126107ab5760206040515f8152f35b346107ab5760403660031901126107ab576113b1612d16565b6004355f52600160205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346107ab5760403660031901126107ab576004355f526002602052602061140d60243560405f206136a0565b905460405160039290921b1c6001600160a01b03168152f35b346107ab575f3660031901126107ab5761143e61301c565b6114466139d1565b600160ff195f5416175f557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346107ab575f3660031901126107ab5760206040517f79e553c6f53701daa99614646285e66adb98ff0fcc1ef165dd2718e5c873bee68152f35b346107ab575f3660031901126107ab5760206040517fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217758152f35b346107ab576020806003193601126107ab5760043561150e613235565b5f8181526008835260409020600a01546001600160a01b03906115349082161515613a14565b815f526008835260405f20600681015490600981015492600a8201541692600b820192835491600f840154421061164f5760038401549161158f600e8601549361157f851515613c72565b611589848461352e565b90613507565b840361161257937f24e88bf6d6cc3807a0c63437666521583d595cca6bc2bc909506961d54cb10b296936115d0608097946115896115e695600c9c9961352e565b988992016115df83825461379c565b905561379c565b90556115f3853383613a87565b60405191825285820152836040820152336060820152a1604051908152f35b60405162461bcd60e51b8152600481018a90526015602482015274105cdcd95c9d0e88085bdc9c1a185b905b5bdd5b9d605a1b6044820152606490fd5b60405162461bcd60e51b815260048101899052602b60248201527f4173736572743a20626c6f636b2e74696d657374616d70203e3d20726566756e60448201526a19111958591b1a5b99505d60aa1b6064820152608490fd5b346107ab5760403660031901126107ab57600435602480356001600160401b038082116107ab57366023830112156107ab578160040135908111611ad0578060051b91602092604051926116fe85830185612e2a565b83528484840191830101913683116107ab5785859101915b838310611ac057505050506117296139d1565b611731613efc565b835f526008825260405f20916118546040519361174d85612dc4565b8054855261175d60018201612e4b565b8386015260ff6002820154161515908160408701526003810154606087015260048101546080870152600581015460a0870152600681015460c0870152600781015460e08701526008810154610100870152600981015461012087015260018060a01b0395610280601488600a850154169384610140850152600b810154610160850152600c81015461018085015260ff600d820154166101a0850152600e8101546101c0850152600f8101546101e085015289601082015416610200850152601181015461022085015260ff6012820154166102408501526013810154610260850152015491015215159081611ab85750613deb565b845f526008815260405f209160048301549380601085015416611878861515613e37565b15611a7457600f840154421015611a3057865f526009835260405f20335f52835260405f20546119f957865f52600b835260405f20335f52835260405f20546119a95782916119079160045416906118dd6040513386820152858152610da981612df4565b9660405197889485938493632c0b0d2160e11b8552600485015260608c8501526064840190612d2c565b90604483015203915afa9283156106d457600e9361192b915f916119925750613eb0565b845f526009815260405f2090335f52524260405f2055018054916001830180931161197f57505533907fd8420f97dab0b820e066ebfad617848c1eab2d260922358cc1d5cdcdc568d5725f80a36001600355005b634e487b7160e01b5f9081526011600452fd5b610f689150833d8511610f6e57610f608183612e2a565b60405162461bcd60e51b8152600481018490526023818801527f4e6f7420656c696769626c6520666f72206120726566756e642028636c61696d60448201526265642960e81b6064820152608490fd5b60405162461bcd60e51b8152600481018490526010818801526f1499599d5b99081c995c5d595cdd195960821b6044820152606490fd5b60405162461bcd60e51b815260048101849052601a818801527f526566756e6420646561646c696e6520686173207061737365640000000000006044820152606490fd5b60405162461bcd60e51b815260048101849052601e818801527f526566756e6420706172616d7320686173206e6f74206265656e2073657400006044820152606490fd5b905087610d69565b8235815291810191859101611716565b82634e487b7160e01b5f5260416004525ffd5b346107ab5760203660031901126107ab576004355f52600860205260405f2080549060018101611b1290612e4b565b61014052600281015460ff1661012052600381015461016052600481015461018052600581015491600682015490600783015491600884015492600985015493600160a01b600190039485600a8801541690600b88015492600c89015494600d8a015460ff1696600e8b015498600f8c01549a60108d0154169b6011810154608052601281015460ff1660a052601381015460e052601401546101005260405160c0526102a09060c051528060c0516020015260c051016101405190611bd791612f0d565b9b61012051151560c051604001526101605160c051606001526101805160c0516080015260c05160a0015260c05160c0015260c05160e0015260c051610100015260c051610120015260c051610140015260c051610160015260c051610180015260c0516101a0015260c0516101c0015260c0516101e0015260c051610200015260805160c051610220015260a05160c051610240015260e05160c05161026001526101005160c051610280015260c051900360c051f35b346107ab575f3660031901126107ab576040517f0000000000000000000000002d5d7d31f671f86c782533cc367f14109a0827126001600160a01b03168152602090f35b346107ab5760203660031901126107ab57600435611cef613235565b805f52600760205260405f2054611d0257005b60a081611d2f7fa93d660afa144f75d00eb088e73864c981f08dd587df69df5de1feccbd476042936137a9565b5060405190606082525f6060830152608060208301525f60808301526040820152a1005b346107ab5760403660031901126107ab57611d6c612d16565b6004355f52600a60205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b346107ab575f3660031901126107ab57602060ff5f54166040519015158152f35b346107ab5760403660031901126107ab57600435602435801515908181036107ab577fd751c6369b67c9aaa0c63a4550e51685b2d4e123d1a30c9b84b97e4a5fa64d2391611e4a602092855f5260088452611e2360018060a01b03600a60405f200154161515613a14565b611e2b61301c565b855f5260088452600260405f20019060ff801983541691151516179055565b604051908152a2005b346107ab5760403660031901126107ab57611e6c612d16565b6004355f52600b60205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b346107ab576003196080368201126107ab576001600160401b03906024358281116107ab57611eca903690600401612d5f565b6044929192358481116107ab57611ee5903690600401612d5f565b946064359081116107ab57611efe903690600401612d5f565b929094611f0c368588612f4d565b96875160208099012088611f6160018060a01b03986040519384928392635f6970c360e01b8452600435600485015260806024850152611f50608485018b8b612ffc565b90848203016044850152878b612ffc565b90606483015203815f8b7f0000000000000000000000004f4495243837681061c4743b74b3eedf548d56a5165af19081156106d4575f91612119575b501561210757611ffe93611fca92611fde611fea936040519586938d85019860408a526060860191612ffc565b90601f199485858403016040860152612ffc565b03908101835282612e2a565b5190205f52600760205260405f2054151590565b156120cb5782606091810103126107ab5781359261203660408285013594013592855f5260088352600a60405f200154161515613a14565b835f526008815260405f2060048101908154612086579183604094928660037ff306a7e4b65014ba5d990b306e17bcc4fc7c87905af7048978eb564cab686b1798960155558351928352820152a2005b60405162461bcd60e51b815260048101849052601e60248201527f4d65726b6c652070726f6f6620726f6f7420686173206265656e2073657400006044820152606490fd5b60405162461bcd60e51b8152600481018590526014602482015273556e74727573746564206d707220736f7572636560601b6044820152606490fd5b604051631403112d60e21b8152600490fd5b6121309150893d8b11610f6e57610f608183612e2a565b89611f9d565b346107ab5760203660031901126107ab576004355f818152600860205260409020600a01546001600160a01b03906121719082161515613a14565b815f52600860205260405f20600e810154916011820154906010830154169260138301928354612218577feb1b7060d355a76dcaf4f04914a551bc4a9480aebf2e5ff33caeb897c7dad29594612213946121eb6121fd946014966121db600f870154421015613f52565b6121e6821515613c72565b613507565b94859384809355015530903390613f9e565b6040805191825233602083015290918291820190565b0390a2005b606460405162461bcd60e51b815260206004820152602060248201527f4173736572743a20726566756e64546f6b656e4465706f7369746564203d20306044820152fd5b346107ab575f3660031901126107ab5761227461301c565b5f5460ff8116156122b15760ff19165f557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606490fd5b346107ab5760403660031901126107ab57612306612d16565b336001600160a01b0382160361232257610bbe9060043561347f565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b346107ab5760403660031901126107ab5760806123a561239d612d16565b600435613cbe565b91604051938452602084015260408301526060820152f35b346107ab5760403660031901126107ab57610bbe60043560026123de612d16565b91805f52602090600182526123f9600160405f200154613378565b5f8181526001835260408082206001600160a01b0390961680835295845290205460ff161561242f575b5f525260405f20613730565b805f526001825260405f20845f52825260405f20600160ff198254161790553384827f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4612423565b346107ab576020806003193601126107ab576001600160a01b03600435818116908190036107ab576124aa613235565b801561261c576040516370a0823160e01b81523060048201528381602481855afa9081156106d4575f916125ef575b5091600554905f5b82811061257a575050508115612535576124fc823383613a87565b604080518381523360208201527f12b2eecb5e61422ef43355aec5faab70327c566a09f5ccb0ff5eca75a5ad40939190a2604051908152f35b60405162461bcd60e51b815260048101849052601960248201527f4173736572743a20756e7573656442616c616e6365203e2030000000000000006044820152606490fd5b805f52600880875260405f208584600a83015416146125d45750815f52865260405f208483601083015416146125ba575b506125b590613a79565b6124e1565b6125b5919560146125cd9201549061379c565b94906125ab565b6125e9915095600c6125b5939701549061379c565b94613a79565b90508381813d8311612615575b6126068183612e2a565b810103126107ab5751846124d9565b503d6125fc565b60405162461bcd60e51b815260048101849052601a60248201527f4173736572743a20746f6b656e416464725f20213d206e756c6c0000000000006044820152606490fd5b346107ab5760203660031901126107ab576004355f5260016020526020600160405f200154604051908152f35b346107ab5760203660031901126107ab576004355f818152600860205260409020600a01546001600160a01b03906126c99082161515613a14565b815f52600860205260405f209060038201546006830154600984015492600a8501541693600b8101948554612773578315612736577f754b20cbde6413b3561d41cba275e6331494efb5b031b17641440f14b7755e48956121eb600c956121e6612213986121fd9761352e565b60405162461bcd60e51b815260206004820152601560248201527404173736572743a20696e766573746f7273203e203605c1b6044820152606490fd5b60405162461bcd60e51b815260206004820152601a60248201527f4173736572743a20746f6b656e4465706f7369746564203d20300000000000006044820152606490fd5b346107ab5760031960c0368201126107ab576001600160401b036024358181116107ab576127ea903690600401612d5f565b6044358381116107ab57612802903690600401612d5f565b6064358581116107ab5761281a903690600401612d5f565b6084359687116107ab5761287e9761288e6128a29561284c61284260209b3690600401612d5f565b9690953691612f4d565b8a8151910120956040519b8c9a8b9a631876eed960e01b8c5260043560048d015260c060248d015260c48c0191612ffc565b91848a84030160448b0152612ffc565b936064870152858403016084860152612ffc565b60a480359083015203815f7f0000000000000000000000004f4495243837681061c4743b74b3eedf548d56a56001600160a01b03165af19081156106d4575f916128ef575b501561210757005b612907915060203d8111610f6e57610f608183612e2a565b816128e7565b346107ab5760203660031901126107ab576004355f526007602052602060405f20541515604051908152f35b346107ab575f3660031901126107ab576040517f0000000000000000000000004f4495243837681061c4743b74b3eedf548d56a56001600160a01b03168152602090f35b346107ab575f3660031901126107ab576040518060065480825282602080930160065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f925f5b858282106129f4575050506129dc92500383612e2a565b6129f0604051928284938452830190612d2c565b0390f35b85548452600195860195889550930192016129c5565b346107ab5760803660031901126107ab57600435612a26612d16565b604490813590606491823591855f526020906008825260018060a01b0394612a5886600a60405f200154161515613a14565b612a6061301c565b875f526008835260405f20916010830191825491888316612c675760405163313ce56760e01b8152908916999086816004818e5afa80156106d45760ff915f91612c4a575b5016988915612c08578715612bc6578815612b8457600a860154168a14612b36578a7fd57b76b8142bba6badaf6950d98fab6fd3a7cc559e1f5e8f965864746d7fe18360808c8c8c8c8c60128d8d878e86601185015587600f8501556bffffffffffffffffffffffff60a01b16179055018460ff1982541617905560405194855284015260408301526060820152a2005b6320b2323960e11b6084927f4173736572743a20726566756e64546f6b656e4164647220213d20746f6b656e88936040519462461bcd60e51b86526004860152602480860152840152820152fd5b507f4173736572743a20726566756e64446561646c696e6541745f203e203000000086916040519262461bcd60e51b84526004840152601d6024840152820152fd5b507f4173736572743a20726566756e64416d6f756e745f203e20300000000000000086916040519262461bcd60e51b8452600484015260196024840152820152fd5b507f4173736572743a20726566756e64546f6b656e446563696d616c73203e20300086916040519262461bcd60e51b84526004840152601f6024840152820152fd5b612c619150883d8a116106cd576106be8183612e2a565b8d612aa5565b5060405162461bcd60e51b815260048101869052601a60248201527f526566756e6420706172616d7320686173206265656e20736574000000000000818b0152fd5b346107ab5760203660031901126107ab576004359063ffffffff60e01b82168092036107ab57602091635a05180f60e01b8114908115612ceb575b5015158152f35b637965db0b60e01b811491508115612d05575b5083612ce4565b6301ffc9a760e01b14905083612cfe565b602435906001600160a01b03821682036107ab57565b9081518082526020808093019301915f5b828110612d4b575050505090565b835185529381019392810192600101612d3d565b9181601f840112156107ab578235916001600160401b0383116107ab57602083818601950101116107ab57565b90600182811c92168015612dba575b6020831014612da657565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612d9b565b6102a081019081106001600160401b03821117612de057604052565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117612de057604052565b608081019081106001600160401b03821117612de057604052565b90601f801991011681019081106001600160401b03821117612de057604052565b9060405191825f8254612e5d81612d8c565b9081845260209460019182811690815f14612eca5750600114612e8c575b505050612e8a92500383612e2a565b565b5f90815285812095935091905b818310612eb2575050612e8a93508201015f8080612e7b565b85548884018501529485019487945091830191612e99565b92505050612e8a94925060ff191682840152151560051b8201015f8080612e7b565b5f5b838110612efd5750505f910152565b8181015183820152602001612eee565b90602091612f2681518092818552858086019101612eec565b601f01601f1916010190565b6001600160401b038111612de057601f01601f191660200190565b929192612f5982612f32565b91612f676040519384612e2a565b8294818452818301116107ab578281602093845f960137010152565b9080601f830112156107ab57816020612f9e93359101612f4d565b90565b9060406003198301126107ab576001600160401b036004358181116107ab5783612fcd91600401612f83565b926024359182116107ab57612f9e91600401612f83565b908160209103126107ab575180151581036107ab5790565b908060209392818452848401375f828201840152601f01601f1916010190565b335f9081527f50efbde2d46c37e9785f1791697f77e94bb7b701e19f1930a668820722d37694602090815260408083205490927fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217759160019060ff1615613083575050505050565b61308c33613560565b9285519161309983612e0f565b60428352858301936060368637835115613221576030855383518210156132215790607860218501536041915b8183116131b45750505061317257604861316e93869361315293613143985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a86015261311a815180928c603789019101612eec565b8401917001034b99036b4b9b9b4b733903937b6329607d1b603784015251809386840190612eec565b01036028810187520185612e2a565b5192839262461bcd60e51b845260048401526024830190612f0d565b0390fd5b60648486519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b909192600f8116601081101561320d576f181899199a1a9b1b9c1cb0b131b232b360811b901a6131e4858761353b565b5360041c9280156131f9575f190191906130c6565b634e487b7160e01b82526011600452602482fd5b634e487b7160e01b83526032600452602483fd5b634e487b7160e01b81526032600452602490fd5b335f9081527fd3ee5543a19ed9f63e85698aded3c4654a61a2e3d0a0400d95a23f1e05193136602090815260408083205490927f79e553c6f53701daa99614646285e66adb98ff0fcc1ef165dd2718e5c873bee69160019060ff161561329c575050505050565b6132a533613560565b928551916132b283612e0f565b60428352858301936060368637835115613221576030855383518210156132215790607860218501536041915b8183116133335750505061317257604861316e93869361315293613143985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a86015261311a815180928c603789019101612eec565b909192600f8116601081101561320d576f181899199a1a9b1b9c1cb0b131b232b360811b901a613363858761353b565b5360041c9280156131f9575f190191906132df565b5f818152600191602091838352604093848220338352845260ff8583205416156133a3575050505050565b6133ac33613560565b928551916133b983612e0f565b60428352858301936060368637835115613221576030855383518210156132215790607860218501536041915b81831161343a5750505061317257604861316e93869361315293613143985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a86015261311a815180928c603789019101612eec565b909192600f8116601081101561320d576f181899199a1a9b1b9c1cb0b131b232b360811b901a61346a858761353b565b5360041c9280156131f9575f190191906133e6565b9060406134bc925f9080825260016020528282209360018060a01b03169384835260205260ff83832054166134bf575b8152600260205220613891565b50565b808252600160205282822084835260205282822060ff1981541690553384827ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b8580a46134af565b8181029291811591840414171561351a57565b634e487b7160e01b5f52601160045260245ffd5b9190820180921161351a57565b90815181101561354c570160200190565b634e487b7160e01b5f52603260045260245ffd5b60405190606082018281106001600160401b03821117612de057604052602a825260208201604036823782511561354c5760309053815160019081101561354c57607860218401536029905b8082116136005750506135bc5790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f81166010811015613657576f181899199a1a9b1b9c1cb0b131b232b360811b901a61362f848661353b565b5360041c918015613643575f1901906135ac565b60245f634e487b7160e01b81526011600452fd5b60245f634e487b7160e01b81526032600452fd5b60065481101561354c5760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01905f90565b805482101561354c575f5260205f2001905f90565b5f8181526007602052604081205461372b57600654600160401b8110156137175790826137036136ed8460016040960160065561366b565b819391549060031b91821b915f19901b19161790565b905560065492815260076020522055600190565b634e487b7160e01b82526041600452602482fd5b905090565b9190600183015f90828252806020526040822054155f1461379657845494600160401b86101561378257836137726136ed886001604098999a018555846136a0565b9055549382526020522055600190565b634e487b7160e01b83526041600452602483fd5b50925050565b9190820391821161351a57565b5f81815260076020526040812054909190801561388c575f1990808201818111613878576006549083820191821161386457808203613830575b505050600654801561381c578101906137fb8261366b565b909182549160031b1b19169055600655815260076020526040812055600190565b634e487b7160e01b84526031600452602484fd5b61384e61383f6136ed9361366b565b90549060031b1c92839261366b565b90558452600760205260408420555f80806137e3565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b85526011600452602485fd5b505090565b9060018201905f92818452826020526040842054908115155f14613977575f19918083018181116139635782549084820191821161394f5780820361391a575b50505080548015613906578201916138e983836136a0565b909182549160031b1b191690555582526020526040812055600190565b634e487b7160e01b86526031600452602486fd5b61393a61392a6136ed93866136a0565b90549060031b1c928392866136a0565b905586528460205260408620555f80806138d1565b634e487b7160e01b88526011600452602488fd5b634e487b7160e01b87526011600452602487fd5b5050505090565b9091613995612f9e93604084526040840190612f0d565b916020818403910152612f0d565b9392916139cc906139be604093606088526060880190612f0d565b908682036020880152612f0d565b930152565b60ff5f54166139dc57565b60405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606490fd5b15613a1b57565b60405162461bcd60e51b815260206004820152601a60248201527f5468652050726f6a65637420646f6573206e6f742065786973740000000000006044820152606490fd5b908160209103126107ab575160ff811681036107ab5790565b5f19811461351a5760010190565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815260808101916001600160401b03831182841017612de057612e8a926040525b604051613b4d916001600160a01b0316613aeb82612df4565b5f806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af13d15613bd5573d91613b3283612f32565b92613b406040519485612e2a565b83523d5f8785013e613bd9565b805190828215928315613bbd575b50505015613b665750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b613bcd9350820181019101612fe4565b5f8281613b5b565b6060915b91929015613c3b5750815115613bed575090565b3b15613bf65790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015613c4e5750805190602001fd5b60405162461bcd60e51b81526020600482015290819061316e906024830190612f0d565b15613c7957565b60405162461bcd60e51b815260206004820152601b60248201527f4173736572743a20726566756e64496e766573746f7273203e203000000000006044820152606490fd5b90915f908282526020916009835260408082209560018060a01b031695868352845260088183205494868452600a815282842088855281528284205497878552600b825283852090855281528284205496845252812084600582015460068301549060098401549360086007820154910154918042105f14613d4857505050505050915b93929190565b613d58828298949596979861352e565b4211613d725750505050613d6c925061379c565b91613d42565b613d939293949550613d8d91613d879161352e565b4261379c565b85613507565b908215613dd7575091613dad613d6c94928894048261352e565b91613db8828261352e565b8311613dc6575b505061379c565b613dd0925061352e565b5f80613dbf565b634e487b7160e01b81526012600452602490fd5b15613df257565b60405162461bcd60e51b815260206004820152601b60248201527f5468652050726f6a65637420646f6573206e6f742061637469766500000000006044820152606490fd5b15613e3e57565b60405162461bcd60e51b815260206004820152602260248201527f4d65726b6c652070726f6f6620726f6f7420686173206e6f74206265656e2073604482015261195d60f21b6064820152608490fd5b602081519101519060208110613ea2575090565b5f199060200360031b1b1690565b15613eb757565b60405162461bcd60e51b815260206004820152601960248201527f4d69736d6174636820696e766573746d656e742070726f6f66000000000000006044820152606490fd5b600260035414613f0d576002600355565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b15613f5957565b60405162461bcd60e51b815260206004820152601e60248201527f526566756e6420646561646c696e6520686173206e6f742070617373656400006044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815260a08101918183106001600160401b03841117612de057612e8a92604052613ad256fea2646970667358221220a90295ff94c3a212f40d4e0a6a4f0f7f6606a0e398d22a09e21127212dec433864736f6c63430008150033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000004f4495243837681061c4743b74b3eedf548d56a50000000000000000000000002d5d7d31f671f86c782533cc367f14109a082712

-----Decoded View---------------
Arg [0] : _gateway (address): 0x4F4495243837681061C4743b74B3eEdf548D56A5
Arg [1] : _gasReceiver (address): 0x2d5d7d31F671F86C782533cc367F14109a082712

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000004f4495243837681061c4743b74b3eedf548d56a5
Arg [1] : 0000000000000000000000002d5d7d31f671f86c782533cc367f14109a082712


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

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