Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0xE7eBA1CEA51EC9B3AcCC16728e3B8786560c59d5
Balance 0 ETH
Nonce 1
Code Size 5301 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

5301 bytes
0x608060405234801561000f575f80fd5b506004361061011c575f3560e01c8063715018a6116100a95780638da5cb5b1161006e5780638da5cb5b1461024a578063b6f3e0871461025a578063bc73b6411461026d578063f2fde38b14610280578063f98b233214610293575f80fd5b8063715018a61461020c57806373c6d87b146102145780638356b148146102275780638456cb591461022f5780638623ec7b14610237575f80fd5b80633d0dc063116100ef5780633d0dc063146101aa5780633f4ba83a146101cb5780635c975abb146101d35780636552d8b4146101e657806369d83ed1146101f9575f80fd5b80630900f010146101205780630cb8150f146101355780631cb928a91461016c578063373f0d4914610197575b5f80fd5b61013361012e366004610ff0565b6102b2565b005b610157610143366004610ff0565b60026020525f908152604090205460ff1681565b60405190151581526020015b60405180910390f35b61017f61017a36600461100b565b6103df565b6040516001600160a01b039091168152602001610163565b60055461017f906001600160a01b031681565b6101bd6101b83660046110bf565b610407565b604051908152602001610163565b61013361044a565b5f5461015790600160a01b900460ff1681565b6101336101f4366004610ff0565b6104d7565b610133610207366004610ff0565b610590565b610133610648565b610133610222366004611154565b61065b565b6004546101bd565b610133610c34565b61017f61024536600461100b565b610cc1565b5f546001600160a01b031661017f565b61013361026836600461120f565b610cd0565b61017f61027b366004610ff0565b610d5f565b61013361028e366004610ff0565b610e66565b6101bd6102a136600461100b565b60016020525f908152604090205481565b6102ba610ea3565b305f5b6003548110156103da575f600382815481106102db576102db611246565b905f5260205f20015f9054906101000a90046001600160a01b03169050826001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561033e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610362919061125a565b6001600160a01b0316036103c75760405163f2fde38b60e01b81526001600160a01b03858116600483015282169063f2fde38b906024015f604051808303815f87803b1580156103b0575f80fd5b505af11580156103c2573d5f803e3d5ffd5b505050505b50806103d281611289565b9150506102bd565b505050565b600481815481106103ee575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f30888888888888886040516020016104279897969594939291906112c3565b604051602081830303815290604052805190602001209050979650505050505050565b610452610ea3565b5f54600160a01b900460ff166104a25760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481d5b9c185d5cd95960821b60448201526064015b60405180910390fd5b5f805460ff60a01b191681556040517f7805862f689e2f13df9f062ff482ad3ad112aca9e0847911ed832e158c525b339190a1565b6104df610ea3565b6001600160a01b0381165f9081526002602052604090205460ff166105395760405162461bcd60e51b815260206004820152601060248201526f34b73b30b634b2103932b1b2b4bb32b960811b6044820152606401610499565b6001600160a01b0381165f81815260026020908152604091829020805460ff1916905590519182527f2771977f239a332de92ab37b7275685268f164e51cda8f1356692695f4708f2f91015b60405180910390a150565b610598610ea3565b6001600160a01b0381165f9081526002602052604090205460ff16156105f55760405162461bcd60e51b815260206004820152601260248201527130b63932b0b23c9030903932b1b2b4bb32b960711b6044820152606401610499565b6001600160a01b0381165f81815260026020908152604091829020805460ff1916600117905590519182527fbec1e1ee82037bd0301ab4218c8c148e3be5be35bdf180546d4ff862df359f359101610585565b610650610ea3565b6106595f610ecf565b565b5f54600160a01b900460ff1615610670575f80fd5b825f036106b75760405162461bcd60e51b8152602060048201526015602482015274616d6f756e742063616e6e6f74206265207a65726f60581b6044820152606401610499565b6001600160a01b03841661070d5760405162461bcd60e51b815260206004820152601860248201527f726563697069656e742063616e6e6f74206265207a65726f00000000000000006044820152606401610499565b6041825161071b919061134e565b156107685760405162461bcd60e51b815260206004820152601860248201527f696e76616c6964207369676e6174757265206c656e67746800000000000000006044820152606401610499565b5f61077889898989898988610407565b5f81815260016020526040902054909150156107d65760405162461bcd60e51b815260206004820152601960248201527f7472616e7366657220686173206265656e20736574746c6564000000000000006044820152606401610499565b5f604184516107e59190611361565b90505f8167ffffffffffffffff81111561080157610801611022565b60405190808252806020026020018201604052801561082a578160200160208202803683370190505b5090505f5b828110156109c4575f61084d8588610848856041611374565b610f1e565b60055460405163babcc53960e01b81526001600160a01b03808416600483015292935091169063babcc53990602401602060405180830381865afa158015610897573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108bb9190611391565b6108fb5760405162461bcd60e51b8152602060048201526011602482015270696e76616c6964207369676e617475726560781b6044820152606401610499565b5f5b828110156109855783818151811061091757610917611246565b60200260200101516001600160a01b0316826001600160a01b0316036109735760405162461bcd60e51b81526020600482015260116024820152706475706c6963617465207769746e65737360781b6044820152606401610499565b8061097d81611289565b9150506108fd565b508083838151811061099957610999611246565b6001600160a01b039092166020928302919091019091015250806109bc81611289565b91505061082f565b5060055f9054906101000a90046001600160a01b03166001600160a01b031663593f69696040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a15573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a3991906113b0565b610a44906002611374565b610a4f836003611374565b11610a955760405162461bcd60e51b8152602060048201526016602482015275696e73756666696369656e74207769746e657373657360501b6044820152606401610499565b5f610a9f8b610d5f565b5f858152600160205260409081902043905551636361ddf360e11b81526001600160a01b038d811660048301528a81166024830152604482018a90529192509082169063c6c3bbe6906064016020604051808303815f875af1158015610b07573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b2b9190611391565b610b6e5760405162461bcd60e51b81526020600482015260146024820152733330b4b632b2103a379036b4b73a103a37b5b2b760611b6044820152606401610499565b6001600160a01b0388165f9081526002602052604090205460ff1615610bee5760405163043b0a1960e11b81526001600160a01b03891690630876143290610bc0908c908f908c908b906004016113c7565b5f604051808303815f87803b158015610bd7575f80fd5b505af1158015610be9573d5f803e3d5ffd5b505050505b837fe24922ac8cf2a1430fb8c7ce6a9125d7db5076a1eb2cefced90e82d6fcb24db083604051610c1e919061141a565b60405180910390a2505050505050505050505050565b610c3c610ea3565b5f54600160a01b900460ff1615610c865760405162461bcd60e51b815260206004820152600e60248201526d185b1c9958591e481c185d5cd95960921b6044820152606401610499565b5f805460ff60a01b1916600160a01b1781556040517f6985a02210a168e66602d3235cb6db0e70f92b3ba4d376a33c0f3d9434bff6259190a1565b600381815481106103ee575f80fd5b610cd8610ea3565b6004805460018181019092557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b039485166001600160a01b0319918216179091556003805492830181555f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b9091018054929093169116179055565b5f805b600454811015610e385760048181548110610d7f57610d7f611246565b5f9182526020909120015460405163babcc53960e01b81526001600160a01b0385811660048301529091169063babcc53990602401602060405180830381865afa158015610dcf573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610df39190611391565b15610e265760038181548110610e0b57610e0b611246565b5f918252602090912001546001600160a01b03169392505050565b80610e3081611289565b915050610d62565b5060035f81548110610e4c57610e4c611246565b5f918252602090912001546001600160a01b031692915050565b610e6e610ea3565b6001600160a01b038116610e9757604051631e4fbdf760e01b81525f6004820152602401610499565b610ea081610ecf565b50565b5f546001600160a01b031633146106595760405163118cdaa760e01b8152336004820152602401610499565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b818101602081015160408201516060909201515f9290831a601b811015610f4d57610f4a601b82611466565b90505b8060ff16601b14158015610f6557508060ff16601c14155b15610f75575f9350505050610fd5565b604080515f81526020810180835289905260ff831691810191909152606081018490526080810183905260019060a0016020604051602081039080840390855afa158015610fc5573d5f803e3d5ffd5b5050506020604051035193505050505b9392505050565b6001600160a01b0381168114610ea0575f80fd5b5f60208284031215611000575f80fd5b8135610fd581610fdc565b5f6020828403121561101b575f80fd5b5035919050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112611045575f80fd5b813567ffffffffffffffff8082111561106057611060611022565b604051601f8301601f19908116603f0116810190828211818310171561108857611088611022565b816040528381528660208588010111156110a0575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f805f805f805f60e0888a0312156110d5575f80fd5b87356110e081610fdc565b965060208801356110f081610fdc565b955060408801359450606088013561110781610fdc565b9350608088013561111781610fdc565b925060a0880135915060c088013567ffffffffffffffff811115611139575f80fd5b6111458a828b01611036565b91505092959891949750929550565b5f805f805f805f80610100898b03121561116c575f80fd5b883561117781610fdc565b9750602089013561118781610fdc565b965060408901359550606089013561119e81610fdc565b945060808901356111ae81610fdc565b935060a0890135925060c089013567ffffffffffffffff808211156111d1575f80fd5b6111dd8c838d01611036565b935060e08b01359150808211156111f2575f80fd5b506111ff8b828c01611036565b9150509295985092959890939650565b5f8060408385031215611220575f80fd5b823561122b81610fdc565b9150602083013561123b81610fdc565b809150509250929050565b634e487b7160e01b5f52603260045260245ffd5b5f6020828403121561126a575f80fd5b8151610fd581610fdc565b634e487b7160e01b5f52601160045260245ffd5b5f6001820161129a5761129a611275565b5060010190565b5f5b838110156112bb5781810151838201526020016112a3565b50505f910152565b5f6bffffffffffffffffffffffff19808b60601b168352808a60601b166014840152808960601b16602884015287603c840152808760601b16605c840152808660601b1660708401525083608483015282516113268160a48501602087016112a1565b9190910160a4019998505050505050505050565b634e487b7160e01b5f52601260045260245ffd5b5f8261135c5761135c61133a565b500690565b5f8261136f5761136f61133a565b500490565b808202811582820484141761138b5761138b611275565b92915050565b5f602082840312156113a1575f80fd5b81518015158114610fd5575f80fd5b5f602082840312156113c0575f80fd5b5051919050565b5f60018060a01b0380871683528086166020840152508360408301526080606083015282518060808401526114038160a08501602087016112a1565b601f01601f19169190910160a00195945050505050565b602080825282518282018190525f9190848201906040850190845b8181101561145a5783516001600160a01b031683529284019291840191600101611435565b50909695505050505050565b60ff818116838216019081111561138b5761138b61127556fea2646970667358221220e84479c582ee604c5d5a1f87d2f561ee560bf63a51e4204834564b18d3428f9864736f6c63430008140033

Verified Source Code Partial Match

Compiler: v0.8.20+commit.a1b79de6 EVM: shanghai Optimization: Yes (200 runs)
TransferValidatorWithPayload.sol 293 lines
// File: @openzeppelin/contracts/utils/Context.sol


// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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: @openzeppelin/contracts/access/Ownable.sol


// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;


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

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

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

// File: iotube/TransferValidatorWithPayload.sol


pragma solidity >= 0.8.0;


interface IAllowlist {
    function isAllowed(address) external view returns (bool);
    function numOfActive() external view returns (uint256);
}

interface IMinter {
    function mint(address, address, uint256) external returns(bool);
    function transferOwnership(address) external;
    function owner() external view returns(address);
}

interface IReceiver {
    function onReceive(address sender, address token, uint256 amount, bytes calldata payload) external;
}

contract TransferValidatorWithPayload is Ownable {
    event Settled(bytes32 indexed key, address[] witnesses);
    event ReceiverAdded(address receiver);
    event ReceiverRemoved(address receiver);
    event Pause();
    event Unpause();
    modifier whenNotPaused() {
        require(!paused);
        _;
    }
    bool public paused;

    mapping(bytes32 => uint256) public settles;
    mapping(address => bool) public receivers;

    IMinter[] public minters;
    IAllowlist[] public tokenLists;
    IAllowlist public witnessList;

    constructor(IAllowlist _witnessList) Ownable(msg.sender) {
        witnessList = _witnessList;
    }

    function pause() public onlyOwner {
        require(!paused, "already paused");
        paused = true;
        emit Pause();
    }

    function unpause() public onlyOwner {
        require(paused, "already unpaused");
        paused = false;
        emit Unpause();
    }

    function generateKey(address cashier, address tokenAddr, uint256 index, address from, address to, uint256 amount, bytes memory payload) public view returns(bytes32) {
        return keccak256(abi.encodePacked(address(this), cashier, tokenAddr, index, from, to, amount, payload));
    }

    function getMinter(address tokenAddr) public view returns (IMinter) {
        for (uint256 i = 0; i < tokenLists.length; i++) {
            if (tokenLists[i].isAllowed(tokenAddr)) {
                return minters[i];
            }
        }
        return minters[0];
    }

    function submit(address cashier, address tokenAddr, uint256 index, address from, address to, uint256 amount, bytes memory signatures, bytes memory payload) public whenNotPaused {
        require(amount != 0, "amount cannot be zero");
        require(to != address(0), "recipient cannot be zero");
        require(signatures.length % 65 == 0, "invalid signature length");
        bytes32 key = generateKey(cashier, tokenAddr, index, from, to, amount, payload);
        require(settles[key] == 0, "transfer has been settled");
        uint256 numOfSignatures = signatures.length / 65;
        address[] memory witnesses = new address[](numOfSignatures);
        for (uint256 i = 0; i < numOfSignatures; i++) {
            address witness = recover(key, signatures, i * 65);
            require(witnessList.isAllowed(witness), "invalid signature");
            for (uint256 j = 0; j < i; j++) {
                require(witness != witnesses[j], "duplicate witness");
            }
            witnesses[i] = witness;
        }
        require(numOfSignatures * 3 > witnessList.numOfActive() * 2, "insufficient witnesses");
        IMinter minter = getMinter(tokenAddr);
        settles[key] = block.number;
        require(minter.mint(tokenAddr, to, amount), "failed to mint token");
        if (receivers[to]) {
            IReceiver(to).onReceive(from, tokenAddr, amount, payload);
        }
        emit Settled(key, witnesses);
    }

    function numOfPairs() external view returns (uint256) {
        return tokenLists.length;
    }

    function addPair(IAllowlist _tokenList, IMinter _minter) external onlyOwner {
        tokenLists.push(_tokenList);
        minters.push(_minter);
    }

    function addReceiver(address _receiver) external onlyOwner {
        require(!receivers[_receiver], "already a receiver");
        receivers[_receiver] = true;
        emit ReceiverAdded(_receiver);
    }

    function removeReceiver(address _receiver) external onlyOwner {
        require(receivers[_receiver], "invalid receiver");
        receivers[_receiver] = false;
        emit ReceiverRemoved(_receiver);
    }

    function upgrade(address _newValidator) external onlyOwner {
        address contractAddr = address(this);
        for (uint256 i = 0; i < minters.length; i++) {
            IMinter minter = minters[i];
            if (minter.owner() == contractAddr) {
                minter.transferOwnership(_newValidator);
            }
        }
    }

    /**
    * @dev Recover signer address from a message by using their signature
    * @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address.
    * @param signature bytes signature, the signature is generated using web3.eth.sign()
    */
    function recover(bytes32 hash, bytes memory signature, uint256 offset)
        internal
        pure
        returns (address)
    {
        bytes32 r;
        bytes32 s;
        uint8 v;

        // Divide the signature in r, s and v variables with inline assembly.
        assembly {
            r := mload(add(signature, add(offset, 0x20)))
            s := mload(add(signature, add(offset, 0x40)))
            v := byte(0, mload(add(signature, add(offset, 0x60))))
        }

        // Version of signature should be 27 or 28, but 0 and 1 are also possible versions
        if (v < 27) {
            v += 27;
        }

        // If the version is correct return the signer address
        if (v != 27 && v != 28) {
            return (address(0));
        }
        // solium-disable-next-line arg-overflow
        return ecrecover(hash, v, r, s);
    }
}

Read Contract

generateKey 0x3d0dc063 → bytes32
getMinter 0xbc73b641 → address
minters 0x8623ec7b → address
numOfPairs 0x8356b148 → uint256
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
receivers 0x0cb8150f → bool
settles 0xf98b2332 → uint256
tokenLists 0x1cb928a9 → address
witnessList 0x373f0d49 → address

Write Contract 9 functions

These functions modify contract state and require a wallet transaction to execute.

addPair 0xb6f3e087
address _tokenList
address _minter
addReceiver 0x69d83ed1
address _receiver
pause 0x8456cb59
No parameters
removeReceiver 0x6552d8b4
address _receiver
renounceOwnership 0x715018a6
No parameters
submit 0x73c6d87b
address cashier
address tokenAddr
uint256 index
address from
address to
uint256 amount
bytes signatures
bytes payload
transferOwnership 0xf2fde38b
address newOwner
unpause 0x3f4ba83a
No parameters
upgrade 0x0900f010
address _newValidator

Recent Transactions

No transactions found for this address