ETH Price: $4,148.24 (+0.11%)

Contract

0xE590a6730D7a8790E99ce3db11466Acb644c3942

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Execute Transact...132310652025-10-09 1:37:565 days ago1759973876IN
0xE590a673...b644c3942
0 ETH0.000001790.00120031
Execute Transact...122579102025-09-27 19:18:4116 days ago1759000721IN
0xE590a673...b644c3942
0 ETH0.00000180.00120031
Execute Transact...115746012025-09-19 21:30:1224 days ago1758317412IN
0xE590a673...b644c3942
0 ETH0.00000280.00120031
Execute Transact...114098972025-09-17 23:45:0826 days ago1758152708IN
0xE590a673...b644c3942
0 ETH0.000001790.00120031
Execute Transact...109755622025-09-12 23:06:1331 days ago1757718373IN
0xE590a673...b644c3942
0 ETH0.000001790.00120031
Execute Transact...106386312025-09-09 1:30:4235 days ago1757381442IN
0xE590a673...b644c3942
0 ETH0.000002130.00120031
Execute Transact...97611662025-08-29 21:46:1745 days ago1756503977IN
0xE590a673...b644c3942
0 ETH0.000001480.00120031
Execute Transact...90848792025-08-22 1:54:5053 days ago1755827690IN
0xE590a673...b644c3942
0 ETH0.000002130.00120031
Execute Transact...90771752025-08-21 23:46:2653 days ago1755819986IN
0xE590a673...b644c3942
0 ETH0.000001810.00120031

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
132310652025-10-09 1:37:565 days ago1759973876
0xE590a673...b644c3942
0 ETH
132310652025-10-09 1:37:565 days ago1759973876
0xE590a673...b644c3942
0 ETH
132310652025-10-09 1:37:565 days ago1759973876
0xE590a673...b644c3942
0 ETH
132310652025-10-09 1:37:565 days ago1759973876
0xE590a673...b644c3942
0 ETH
132310652025-10-09 1:37:565 days ago1759973876
0xE590a673...b644c3942
0 ETH
132310652025-10-09 1:37:565 days ago1759973876
0xE590a673...b644c3942
0 ETH
132310652025-10-09 1:37:565 days ago1759973876
0xE590a673...b644c3942
0 ETH
132310652025-10-09 1:37:565 days ago1759973876
0xE590a673...b644c3942
0 ETH
122579102025-09-27 19:18:4116 days ago1759000721
0xE590a673...b644c3942
0 ETH
122579102025-09-27 19:18:4116 days ago1759000721
0xE590a673...b644c3942
0 ETH
122579102025-09-27 19:18:4116 days ago1759000721
0xE590a673...b644c3942
0 ETH
122579102025-09-27 19:18:4116 days ago1759000721
0xE590a673...b644c3942
0 ETH
122579102025-09-27 19:18:4116 days ago1759000721
0xE590a673...b644c3942
0 ETH
122579102025-09-27 19:18:4116 days ago1759000721
0xE590a673...b644c3942
0 ETH
122579102025-09-27 19:18:4116 days ago1759000721
0xE590a673...b644c3942
0 ETH
122579102025-09-27 19:18:4116 days ago1759000721
0xE590a673...b644c3942
0 ETH
115746012025-09-19 21:30:1224 days ago1758317412
0xE590a673...b644c3942
0 ETH
115746012025-09-19 21:30:1224 days ago1758317412
0xE590a673...b644c3942
0 ETH
115746012025-09-19 21:30:1224 days ago1758317412
0xE590a673...b644c3942
0 ETH
115746012025-09-19 21:30:1224 days ago1758317412
0xE590a673...b644c3942
0 ETH
115746012025-09-19 21:30:1224 days ago1758317412
0xE590a673...b644c3942
0 ETH
115746012025-09-19 21:30:1224 days ago1758317412
0xE590a673...b644c3942
0 ETH
115746012025-09-19 21:30:1224 days ago1758317412
0xE590a673...b644c3942
0 ETH
115746012025-09-19 21:30:1224 days ago1758317412
0xE590a673...b644c3942
0 ETH
115746012025-09-19 21:30:1224 days ago1758317412
0xE590a673...b644c3942
0 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OneSig

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.22;

import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { MultiSig } from "./MultiSig.sol";
import { ExecutorStore } from "./ExecutorStore.sol";

/**
 * @title OneSig
 * @author @TRileySchwarz, @Clearwood, @HansonYip, @mok-lz
 * @notice A multi-chain enabled contract that uses a Merkle tree of transaction leaves.
 *         It allows transactions to be signed once (off-chain) and then executed on multiple chains,
 *         provided the Merkle proof is valid and the threshold of signers is met.
 * @dev Inherits from MultiSig for signature threshold logic.
 */
contract OneSig is MultiSig, ReentrancyGuard, ExecutorStore {
    /// @notice The version string of the OneSig contract.
    string public constant VERSION = "0.0.1";

    uint8 public constant LEAF_ENCODING_VERSION = 1;

    /**
     * @dev EIP-191 defines the format of the signature prefix.
     *      See https://eips.ethereum.org/EIPS/eip-191
     */
    string private constant EIP191_PREFIX_FOR_EIP712 = "\x19\x01";

    /**
     * @dev EIP-712 domain separator type-hash.
     *      See https://eips.ethereum.org/EIPS/eip-712
     */
    bytes32 private constant EIP712DOMAIN_TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    /**
     * @dev This domain separator is used to generate a signature hash for the merkle root,
     *      specifically using chainId = 1 (Ethereum Mainnet) and verifyingContract = 0xdEaD.
     *      This ensures that the same merkle root signatures can be used across different chains
     *      because they are all signed with this consistent "fake" domain.
     *
     *      In other words, to verify the merkle root with the same signatures on different chains,
     *      we use the same chainId (1) and verifyingContract (0xdEaD) in the EIP-712 domain.
     */
    bytes32 private constant DOMAIN_SEPARATOR =
        keccak256(
            abi.encode(
                EIP712DOMAIN_TYPE_HASH,
                keccak256(bytes("OneSig")), // this contract name
                keccak256(bytes(VERSION)), // version
                1, // Ethereum mainnet chainId
                address(0xdEaD) // verifyingContract
            )
        );

    /**
     * @dev The type-hash of the data being signed to authorize a merkle root.
     */
    bytes32 private constant SIGN_MERKLE_ROOT_TYPE_HASH =
        keccak256("SignMerkleRoot(bytes32 seed,bytes32 merkleRoot,uint256 expiry)");

    /**
     * @notice The OneSig ID of the contract.
     * @dev Because the oneSigId is part of the leaf, the same signatures can be used on different chains,
     *      while leaving each transaction to be targetted towards one
     */
    uint64 public immutable ONE_SIG_ID;

    /**
     * @notice A random seed encoded into the signatures/root.
     * @dev Allows for a previously signed, but unexecuted, transaction(s) to be 'revoked' by changing the seed.
     */
    bytes32 public seed;

    /**
     * @notice A sequential nonce to prevent replay attacks and enforce transaction ordering.
     */
    uint64 public nonce;

    /// @notice Emitted when the seed is updated.
    event SeedSet(bytes32 seed);

    /// @notice Emitted when a transaction is executed.
    /// @param merkleRoot The merkle root used to authorize the transaction.
    /// @param nonce The nonce of the transaction.
    event TransactionExecuted(bytes32 merkleRoot, uint256 nonce);

    /// @notice Error thrown when a merkle proof is invalid or the nonce does not match the expected value.
    error InvalidProofOrNonce();

    /// @notice Error thrown when a merkle root has expired (past the _expiry timestamp).
    error MerkleRootExpired();

    /// @notice Error thrown when a call in the transaction array fails.
    /// @param index The index of the failing call within the transaction.
    error ExecutionFailed(uint256 index);

    /// @notice Error thrown when a function is not called from an executor or signer.
    error OnlyExecutorOrSigner();

    /**
     * @notice Call to be executed as part of a Transaction.calls.
     *  - OneSig -> [Arbitrary contract].
     *  - e.g., setPeer(dstEid, remoteAddress).
     * @param to Address of the contract for this data to be 'called' on.
     * @param value Amount of ether to send with this call.
     * @param data Encoded data to be sent to the contract (calldata).
     */
    struct Call {
        address to;
        uint256 value;
        bytes data;
    }

    /**
     * @notice Single call to the OneSig contract (address(this)).
     *  - EOA -> OneSig
     *  - This struct is 1:1 with a 'leaf' in the merkle tree.
     *  - Execution of the underlying calls are atomic.
     *  - Cannot be processed until the previous leaf (nonce-ordered) has been executed successfully.
     * @param calls List of calls to be made.
     * @param proof Merkle proof to verify the transaction.
     */
    struct Transaction {
        Call[] calls;
        bytes32[] proof;
    }

    /**
     * @dev Restricts access to functions so they can only be called via an executor, OR a multisig signer.
     */
    modifier onlyExecutorOrSigner() {
        if (!canExecuteTransaction(msg.sender)) revert OnlyExecutorOrSigner();
        _;
    }

    /**
     * @notice Constructor to initialize the OneSig contract.
     * @dev Inherits MultiSig(_signers, _threshold).
     * @param _oneSigId A unique identifier per deployment, (typically block.chainid).
     * @param _signers The list of signers authorized to sign transactions.
     * @param _threshold The initial threshold of signers required to execute a transaction.
     * @param _executors The list of executors authorized to execute transactions.
     * @param _executorRequired If executors are required to execute transactions.
     * @param _seed The random seed to encode into the signatures/root.
     */
    constructor(
        uint64 _oneSigId,
        address[] memory _signers,
        uint256 _threshold,
        address[] memory _executors,
        bool _executorRequired,
        bytes32 _seed
    ) MultiSig(_signers, _threshold) ExecutorStore(_executors, _executorRequired) {
        ONE_SIG_ID = _oneSigId;
        _setSeed(_seed);
    }

    /**
     * @notice Internal method to set the contract's seed.
     * @param _seed The new seed value.
     */
    function _setSeed(bytes32 _seed) internal virtual {
        seed = _seed;
        emit SeedSet(_seed);
    }

    /**
     * @notice Sets the contract's seed.
     * @dev Only callable via MultiSig functionality (i.e., requires threshold signatures from signers).
     * @param _seed The new seed value.
     */
    function setSeed(bytes32 _seed) public virtual onlySelfCall {
        _setSeed(_seed);
    }

    /**
     * @notice Executes a single transaction (which corresponds to a leaf in the merkle tree) if valid signatures are provided.
     * @dev '_transaction' corresponds 1:1 with a leaf. This function can be called by anyone (permissionless),
     *      provided the merkle root is verified with sufficient signatures.
     * @param _transaction The transaction data struct, including calls and proof.
     * @param _merkleRoot The merkle root that authorizes this transaction.
     * @param _expiry The timestamp after which the merkle root expires.
     * @param _signatures Signatures from signers that meet the threshold.
     */
    function executeTransaction(
        Transaction calldata _transaction,
        bytes32 _merkleRoot,
        uint256 _expiry,
        bytes calldata _signatures
    ) public payable virtual nonReentrant onlyExecutorOrSigner {
        // Verify the merkle root and signatures
        verifyMerkleRoot(_merkleRoot, _expiry, _signatures);

        // Verify that this transaction matches the merkle root (using its proof)
        verifyTransactionProof(_merkleRoot, _transaction);

        // Increment nonce before execution to prevent replay
        uint256 n = nonce++;

        // Execute all calls atomically
        for (uint256 i = 0; i < _transaction.calls.length; i++) {
            (bool success, ) = _transaction.calls[i].to.call{ value: _transaction.calls[i].value }(
                _transaction.calls[i].data
            );

            // Revert if the call fails
            if (!success) revert ExecutionFailed(i);
        }

        emit TransactionExecuted(_merkleRoot, n);
    }

    /**
     * @notice Validates the signatures on a given merkle root.
     * @dev Reverts if the merkle root is expired or signatures do not meet the threshold.
     * @param _merkleRoot The merkle root to verify.
     * @param _expiry The timestamp after which the merkle root becomes invalid.
     * @param _signatures The provided signatures.
     */
    function verifyMerkleRoot(bytes32 _merkleRoot, uint256 _expiry, bytes calldata _signatures) public view {
        // Check expiry
        if (block.timestamp > _expiry) revert MerkleRootExpired();

        // Compute the EIP-712 hash
        bytes32 digest = keccak256(
            abi.encodePacked(
                EIP191_PREFIX_FOR_EIP712,
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(SIGN_MERKLE_ROOT_TYPE_HASH, seed, _merkleRoot, _expiry))
            )
        );

        // Verify the threshold signatures
        verifySignatures(digest, _signatures);
    }

    /**
     * @notice Verifies that the provided merkle proof matches the current transaction leaf under the merkle root.
     * @dev Reverts if the proof is invalid or the nonce doesn't match the expected value.
     * @param _merkleRoot The merkle root being used.
     * @param _transaction The transaction data containing proof and calls.
     */
    function verifyTransactionProof(bytes32 _merkleRoot, Transaction calldata _transaction) public view {
        bytes32 leaf = encodeLeaf(nonce, _transaction.calls);
        bool valid = MerkleProof.verifyCalldata(_transaction.proof, _merkleRoot, leaf);
        if (!valid) revert InvalidProofOrNonce();
    }

    /**
     * @notice Double encodes the transaction leaf for inclusion in the merkle tree.
     * @param _nonce The nonce of the transaction.
     * @param _calls The calls to be made in this transaction.
     * @return The keccak256 hash of the encoded leaf.
     */
    function encodeLeaf(uint64 _nonce, Call[] calldata _calls) public view returns (bytes32) {
        return
            keccak256(
                abi.encodePacked(
                    keccak256(
                        abi.encodePacked(
                            LEAF_ENCODING_VERSION,
                            ONE_SIG_ID,
                            bytes32(uint256(uint160(address(this)))), // convert address(this) into bytes32
                            _nonce,
                            abi.encode(_calls)
                        )
                    )
                )
            );
    }

    /**
     * @notice Checks if the a given address can execute a transaction.
     * @param _sender The address of the message sender.
     * @return True if executeTransaction can be called by the executor, otherwise false.
     */
    function canExecuteTransaction(address _sender) public view returns (bool) {
        // If the flag is set to false, then ANYONE can execute permissionlessly, otherwise the msg.sender must be a executor, or a signer
        return (!executorRequired || isExecutor(_sender) || isSigner(_sender));
    }

    /**
     * @notice Fallback function to receive ether.
     * @dev Allows the contract to accept ETH.
     */
    receive() external payable {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @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;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    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
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // 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;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

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

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

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

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

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

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

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ? {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

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

        return (signer, RecoverError.NoError, bytes32(0));
    }

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

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.20;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the Merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates Merkle trees that are safe
 * against this attack out of the box.
 */
library MerkleProof {
    /**
     *@dev The multiproof provided is not valid.
     */
    error MerkleProofInvalidMultiproof();

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     */
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proofLen != totalHashes + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            if (proofPos != proofLen) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proofLen != totalHashes + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            if (proofPos != proofLen) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Sorts the pair (a, b) and hashes the result.
     */
    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    /**
     * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
     */
    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @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 is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 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 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

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

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

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

            // Delete the tracked position for the deleted slot
            delete set._positions[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._positions[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;
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.22;

import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { SelfCallable } from "./lib/SelfCallable.sol";

/**
 * @title ExecutorStore
 * @notice Abstract contract that manages a set of executors and a whether they are required.
 * @dev Uses EnumerableSet to store executor addresses
 */
abstract contract ExecutorStore is SelfCallable {
    using EnumerableSet for EnumerableSet.AddressSet;

    /**
     * @dev Set of available executors for the MultiSig.
     */
    EnumerableSet.AddressSet internal executorSet;

    /**
     * @notice Whether the executor permission is required to execute a transaction.
     */
    bool public executorRequired;

    /// @notice Error thrown when an executor address is invalid.
    /// @dev This error is thrown when the address is zero.
    error InvalidExecutor();

    /// @notice Error thrown when attempting to add an execute who is already active.
    /// @param executor The address of the executor.
    error ExecutorAlreadyActive(address executor);

    /// @notice Error thrown when attempting to remove an execute who is not found.
    /// @param executor The address of the executor.
    error ExecutorNotFound(address executor);

    /**
     * @notice Emitted when an executor's active status is updated.
     * @param executor The address of the executor.
     * @param active True if added, false if removed.
     */
    event ExecutorSet(address indexed executor, bool active);

    /**
     * @notice Emitted when the executor required state is updated.
     * @param required The new state
     */
    event ExecutorRequiredSet(bool required);

    /**
     * @dev Initializes the ExecutorStore with a list of executors and sets whether executors are required.
     * @param _executors Array of executor addresses, can be empty.
     * @dev If the array is empty, executorsRequired will be set to false.
     */
    constructor(address[] memory _executors, bool _executorRequired) {
        for (uint256 i = 0; i < _executors.length; i++) {
            _addExecutor(_executors[i]);
        }
        _setExecutorRequired(_executorRequired);
    }

    /**
     * @dev Sets whether executors are required.
     * @param _executorRequired The new threshold value.
     */
    function setExecutorRequired(bool _executorRequired) external onlySelfCall {
        _setExecutorRequired(_executorRequired);
    }

    /**
     * @dev Internal function to set whether executors are required for this MultiSig.
     * @param _executorRequired The new value.
     */
    function _setExecutorRequired(bool _executorRequired) internal {
        executorRequired = _executorRequired;
        emit ExecutorRequiredSet(_executorRequired);
    }

    /**
     * @notice Adds or removes an executor from this MultiSig.
     * @dev Only callable via the MultiSig contract itself.
     * @param _executor The address of the executor to add/remove.
     * @param _active True to add executor, false to remove executor.
     */
    function setExecutor(address _executor, bool _active) external onlySelfCall {
        if (_active) {
            _addExecutor(_executor);
        } else {
            _removeExecutor(_executor);
        }
    }

    /**
     * @dev Internal function to add an executor.
     * @param _executor The address of the executor to add.
     */
    function _addExecutor(address _executor) internal {
        if (_executor == address(0)) revert InvalidExecutor();
        if (!executorSet.add(_executor)) revert ExecutorAlreadyActive(_executor);
        emit ExecutorSet(_executor, true);
    }

    /**
     * @dev Internal function to remove an executor.
     * @param _executor The address of the executor to remove.
     */
    function _removeExecutor(address _executor) internal {
        if (!executorSet.remove(_executor)) revert ExecutorNotFound(_executor);
        emit ExecutorSet(_executor, false);
    }

    /**
     * @notice Returns the list of all active executors.
     * @return An array of addresses representing the current set of executors.
     */
    function getExecutors() public view returns (address[] memory) {
        return executorSet.values();
    }

    /**
     * @notice Checks if a given address is in the set of executors.
     * @param _executor The address to check.
     * @return True if the address is a executor, otherwise false.
     */
    function isExecutor(address _executor) public view returns (bool) {
        return executorSet.contains(_executor);
    }

    /**
     * @notice Returns the total number of active executors.
     * @return The number of executors currently active.
     */
    function totalExecutors() public view returns (uint256) {
        return executorSet.length();
    }
}

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.22;

import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { SelfCallable } from "./lib/SelfCallable.sol";

/**
 * @title MultiSig
 * @notice Abstract contract that manages a set of signers and a signature threshold.
 *         Designed to be inherited by contracts requiring multi-signature verification.
 * @dev Uses EnumerableSet to store signer addresses and ECDSA for signature recovery.
 */
abstract contract MultiSig is SelfCallable {
    using EnumerableSet for EnumerableSet.AddressSet;

    /**
     * @dev Set of available signers for the MultiSig.
     */
    EnumerableSet.AddressSet internal signerSet;

    /**
     * @notice The number of signatures required to execute a transaction.
     */
    uint256 public threshold;

    /// @notice Error thrown when a signer address is invalid.
    error InvalidSigner();

    /// @notice Error thrown when the threshold is set to zero.
    error ZeroThreshold();

    /// @notice Error thrown when the total number of signers is less than the threshold.
    /// @param totalSigners The current number of signers.
    /// @param threshold The required threshold.
    error TotalSignersLessThanThreshold(uint256 totalSigners, uint256 threshold);

    /// @notice Error thrown when attempting to add a signer who is already active.
    /// @param signer The address of the signer.
    error SignerAlreadyAdded(address signer);

    /// @notice Error thrown when attempting to remove a signer who is not found.
    /// @param signer The address of the signer.
    error SignerNotFound(address signer);

    /// @notice Error thrown when there is a signature format error or mismatch in length.
    error SignatureError();

    /// @notice Error thrown when signers are not sorted in ascending order (prevents duplicates).
    error UnsortedSigners();

    /**
     * @notice Emitted when a signer's active status is updated.
     * @param signer The address of the signer.
     * @param active True if added, false if removed.
     */
    event SignerSet(address indexed signer, bool active);

    /**
     * @notice Emitted when the threshold for signatures is set.
     * @param threshold The new threshold.
     */
    event ThresholdSet(uint256 threshold);

    /**
     * @dev The length of a single signature in bytes (r=32, s=32, v=1).
     */
    uint8 constant SIGNATURE_LENGTH = 65;

    /**
     * @dev Initializes the MultiSig with a list of signers and sets the signature threshold.
     * @param _signers Array of signer addresses.
     * @param _threshold The initial threshold for signatures.
     */
    constructor(address[] memory _signers, uint256 _threshold) {
        for (uint256 i = 0; i < _signers.length; i++) {
            _addSigner(_signers[i]);
        }
        _setThreshold(_threshold);
    }

    /**
     * @notice Allows the MultiSig contract to update the signature threshold.
     * @dev This function can only be called by the MultiSig contract itself.
     * @param _threshold The new threshold value.
     */
    function setThreshold(uint256 _threshold) external onlySelfCall {
        _setThreshold(_threshold);
    }

    /**
     * @dev Internal function to set the threshold for this MultiSig.
     *      - The threshold must be greater than zero.
     *      - The threshold must be less than or equal to the number of signers.
     * @param _threshold The new threshold value.
     */
    function _setThreshold(uint256 _threshold) internal {
        if (_threshold == 0) revert ZeroThreshold();
        if (totalSigners() < _threshold) revert TotalSignersLessThanThreshold(totalSigners(), _threshold);

        threshold = _threshold;
        emit ThresholdSet(_threshold);
    }

    /**
     * @notice Adds or removes a signer from this MultiSig.
     * @dev Only callable via the MultiSig contract itself.
     * @param _signer The address of the signer to add/remove.
     * @param _active True to add signer, false to remove signer.
     */
    function setSigner(address _signer, bool _active) external onlySelfCall {
        if (_active) {
            _addSigner(_signer);
        } else {
            _removeSigner(_signer);
        }
    }

    /**
     * @dev Internal function to add a signer.
     *      - `address(0)` is not a valid signer.
     *      - A signer cannot be added twice.
     * @param _signer The address of the signer to add.
     */
    function _addSigner(address _signer) internal {
        if (_signer == address(0)) revert InvalidSigner();
        if (!signerSet.add(_signer)) revert SignerAlreadyAdded(_signer);

        emit SignerSet(_signer, true);
    }

    /**
     * @dev Internal function to remove a signer.
     *      - Signer must be part of the existing set of signers.
     *      - The threshold must be less than or equal to the number of remaining signers.
     * @param _signer The address of the signer to remove.
     */
    function _removeSigner(address _signer) internal {
        if (!signerSet.remove(_signer)) revert SignerNotFound(_signer);
        if (totalSigners() < threshold) revert TotalSignersLessThanThreshold(totalSigners(), threshold);

        emit SignerSet(_signer, false);
    }

    /**
     * @notice Verifies signatures on a given digest against the threshold.
     * @dev Verifies that exactly `threshold` signatures are present, sorted by ascending signer addresses.
     * @param _digest The message digest (hash) being signed.
     * @param _signatures The concatenated signatures.
     */
    function verifySignatures(bytes32 _digest, bytes calldata _signatures) public view {
        verifyNSignatures(_digest, _signatures, threshold);
    }

    /**
     * @notice Verifies N signatures on a given digest.
     * @dev Reverts if:
     *       - The threshold passed is zero.
     *       - The number of signatures doesn't match N (each signature is 65 bytes).
     *       - The signers are not strictly increasing (to prevent duplicates).
     *       - Any signer is not in the set of authorized signers.
     * @param _digest The message digest (hash) being signed.
     * @param _signatures The concatenated signatures.
     * @param _threshold The required number of valid signatures.
     */
    function verifyNSignatures(bytes32 _digest, bytes calldata _signatures, uint256 _threshold) public view {
        if (_threshold == 0) revert ZeroThreshold();
        // Each signature is SIGNATURE_LENGTH (65) bytes (r=32, s=32, v=1).
        if ((_signatures.length % SIGNATURE_LENGTH) != 0) revert SignatureError();
        uint256 signaturesCount = _signatures.length / SIGNATURE_LENGTH;
        if (signaturesCount < _threshold) revert SignatureError();

        // There cannot be a signer with address 0, so we start with address(0) to ensure ascending order.
        address lastSigner = address(0);

        for (uint256 i = 0; i < signaturesCount; i++) {
            // Extract a single signature (SIGNATURE_LENGTH (65) bytes) at a time.
            bytes calldata signature = _signatures[i * SIGNATURE_LENGTH:(i + 1) * SIGNATURE_LENGTH];
            address currentSigner = ECDSA.recover(_digest, signature);

            // Check ordering to avoid duplicates and ensure strictly increasing addresses.
            if (currentSigner <= lastSigner) revert UnsortedSigners();
            // Check if the signer is in our set.
            if (!isSigner(currentSigner)) revert SignerNotFound(currentSigner);
            lastSigner = currentSigner;
        }
    }

    /**
     * @notice Returns the list of all active signers.
     * @return An array of addresses representing the current set of signers.
     */
    function getSigners() public view returns (address[] memory) {
        return signerSet.values();
    }

    /**
     * @notice Checks if a given address is in the set of signers.
     * @param _signer The address to check.
     * @return True if the address is a signer, otherwise false.
     */
    function isSigner(address _signer) public view returns (bool) {
        return signerSet.contains(_signer);
    }

    /**
     * @notice Returns the total number of active signers.
     * @return The number of signers currently active.
     */
    function totalSigners() public view returns (uint256) {
        return signerSet.length();
    }
}

File 8 of 8 : SelfCallable.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.22;

abstract contract SelfCallable {
    /// @notice Error thrown when attempting to call a function from an invalid address.
    error OnlySelfCall();

    /**
     * @dev Restricts access to functions so they can only be called via this contract itself.
     */
    modifier onlySelfCall() {
        if (msg.sender != address(this)) revert OnlySelfCall();
        _;
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"uint64","name":"_oneSigId","type":"uint64"},{"internalType":"address[]","name":"_signers","type":"address[]"},{"internalType":"uint256","name":"_threshold","type":"uint256"},{"internalType":"address[]","name":"_executors","type":"address[]"},{"internalType":"bool","name":"_executorRequired","type":"bool"},{"internalType":"bytes32","name":"_seed","type":"bytes32"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ExecutionFailed","type":"error"},{"inputs":[{"internalType":"address","name":"executor","type":"address"}],"name":"ExecutorAlreadyActive","type":"error"},{"inputs":[{"internalType":"address","name":"executor","type":"address"}],"name":"ExecutorNotFound","type":"error"},{"inputs":[],"name":"InvalidExecutor","type":"error"},{"inputs":[],"name":"InvalidProofOrNonce","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"MerkleRootExpired","type":"error"},{"inputs":[],"name":"OnlyExecutorOrSigner","type":"error"},{"inputs":[],"name":"OnlySelfCall","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"SignatureError","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"SignerAlreadyAdded","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"SignerNotFound","type":"error"},{"inputs":[{"internalType":"uint256","name":"totalSigners","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"TotalSignersLessThanThreshold","type":"error"},{"inputs":[],"name":"UnsortedSigners","type":"error"},{"inputs":[],"name":"ZeroThreshold","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"required","type":"bool"}],"name":"ExecutorRequiredSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"executor","type":"address"},{"indexed":false,"internalType":"bool","name":"active","type":"bool"}],"name":"ExecutorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"seed","type":"bytes32"}],"name":"SeedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"signer","type":"address"},{"indexed":false,"internalType":"bool","name":"active","type":"bool"}],"name":"SignerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"ThresholdSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"TransactionExecuted","type":"event"},{"inputs":[],"name":"LEAF_ENCODING_VERSION","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_SIG_ID","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"}],"name":"canExecuteTransaction","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"_nonce","type":"uint64"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct OneSig.Call[]","name":"_calls","type":"tuple[]"}],"name":"encodeLeaf","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct OneSig.Call[]","name":"calls","type":"tuple[]"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct OneSig.Transaction","name":"_transaction","type":"tuple"},{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"_expiry","type":"uint256"},{"internalType":"bytes","name":"_signatures","type":"bytes"}],"name":"executeTransaction","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"executorRequired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExecutors","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSigners","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_executor","type":"address"}],"name":"isExecutor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"isSigner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"seed","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_executor","type":"address"},{"internalType":"bool","name":"_active","type":"bool"}],"name":"setExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_executorRequired","type":"bool"}],"name":"setExecutorRequired","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_seed","type":"bytes32"}],"name":"setSeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"bool","name":"_active","type":"bool"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_threshold","type":"uint256"}],"name":"setThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"threshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalExecutors","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSigners","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"_expiry","type":"uint256"},{"internalType":"bytes","name":"_signatures","type":"bytes"}],"name":"verifyMerkleRoot","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_digest","type":"bytes32"},{"internalType":"bytes","name":"_signatures","type":"bytes"},{"internalType":"uint256","name":"_threshold","type":"uint256"}],"name":"verifyNSignatures","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_digest","type":"bytes32"},{"internalType":"bytes","name":"_signatures","type":"bytes"}],"name":"verifySignatures","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"components":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct OneSig.Call[]","name":"calls","type":"tuple[]"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct OneSig.Transaction","name":"_transaction","type":"tuple"}],"name":"verifyTransactionProof","outputs":[],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040523480156200001157600080fd5b50604051620021d3380380620021d38339810160408190526200003491620004cd565b8282868660005b82518110156200007b57620000728382815181106200005e576200005e6200057d565b60200260200101516200010360201b60201c565b6001016200003b565b506200008781620001a9565b5050600160035560005b8251811015620000d157620000c8838281518110620000b457620000b46200057d565b60200260200101516200024760201b60201c565b60010162000091565b50620000dd81620002e3565b50506001600160401b038616608052620000f78162000325565b50505050505062000593565b6001600160a01b0381166200012b57604051632057875960e21b815260040160405180910390fd5b620001386000826200035b565b62000166576040516327ec359360e21b81526001600160a01b03821660048201526024015b60405180910390fd5b604051600181526001600160a01b038216907ffc4acb499491cd850a8a21ab98c7f128850c0f0e5f1a875a62b7fa055c2ecf19906020015b60405180910390a250565b80600003620001cb5760405163831761d760e01b815260040160405180910390fd5b80620001d66200037b565b10156200020b57620001e76200037b565b60405162daa43760e71b81526004810191909152602481018290526044016200015d565b60028190556040518181527f6e8a187d7944998085dbd1f16b84c51c903bb727536cdba86962439aded2cfd7906020015b60405180910390a150565b6001600160a01b0381166200026f5760405163710c949760e01b815260040160405180910390fd5b6200027c6004826200035b565b620002a65760405163c06cd70160e01b81526001600160a01b03821660048201526024016200015d565b604051600181526001600160a01b038216907f278b09622564dd3991fe7744514513d64ea2c8ed2b2b9ec1150ad964fde80a99906020016200019e565b6006805460ff19168215159081179091556040519081527faf318623f8b8327d379709bc2f6846f21be20c305509acd41dccf7e07a485347906020016200023c565b60078190556040518181527f36d9c0a688d366a03517cca4843dfff4e45db3869d5fd86fb9d15a1b45ce0e5a906020016200023c565b600062000372836001600160a01b0384166200038d565b90505b92915050565b60006200038881620003df565b905090565b6000818152600183016020526040812054620003d65750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000375565b50600062000375565b600062000375825490565b634e487b7160e01b600052604160045260246000fd5b80516001600160a01b03811681146200041857600080fd5b919050565b600082601f8301126200042f57600080fd5b815160206001600160401b03808311156200044e576200044e620003ea565b8260051b604051601f19603f83011681018181108482111715620004765762000476620003ea565b60405293845260208187018101949081019250878511156200049757600080fd5b6020870191505b84821015620004c257620004b28262000400565b835291830191908301906200049e565b979650505050505050565b60008060008060008060c08789031215620004e757600080fd5b86516001600160401b0380821682146200050057600080fd5b6020890151919750808211156200051657600080fd5b620005248a838b016200041d565b96506040890151955060608901519150808211156200054257600080fd5b506200055189828a016200041d565b935050608087015180151581146200056857600080fd5b8092505060a087015190509295509295509295565b634e487b7160e01b600052603260045260246000fd5b608051611c1d620005b66000396000818161043c0152610afe0152611c1d6000f3fe60806040526004361061016a5760003560e01c8063960bfe04116100d1578063d63f97c91161008a578063e9ec3f5811610064578063e9ec3f581461042a578063ef09e78f1461045e578063f621ff6014610473578063ffa1ad741461048857600080fd5b8063d63f97c9146103ca578063debfda30146103ea578063e1ed81761461040a57600080fd5b8063960bfe04146102fd578063affed0e01461031d578063b677cde414610355578063c7a823e014610375578063cf734e8a14610395578063d1a62648146103b557600080fd5b8063606c22e011610123578063606c22e0146102485780636419ebde146102725780637d94792a146102925780637df73e27146102a85780638e6725bd146102c857806394cf795e146102db57600080fd5b80631e1bff3f146101765780632b03f6ab1461019857806331cb6105146101b8578063347024ff146101d857806342cde4e8146101f857806349dcfdd51461022157600080fd5b3661017157005b600080fd5b34801561018257600080fd5b506101966101913660046114ce565b6104c6565b005b3480156101a457600080fd5b506101966101b3366004611549565b610505565b3480156101c457600080fd5b506101966101d33660046114ce565b6106a7565b3480156101e457600080fd5b506101966101f336600461159b565b6106e2565b34801561020457600080fd5b5061020e60025481565b6040519081526020015b60405180910390f35b34801561022d57600080fd5b50610236600181565b60405160ff9091168152602001610218565b34801561025457600080fd5b506006546102629060ff1681565b6040519015158152602001610218565b34801561027e57600080fd5b5061019661028d3660046115b4565b610711565b34801561029e57600080fd5b5061020e60075481565b3480156102b457600080fd5b506102626102c3366004611606565b610891565b6101966102d6366004611639565b6108a3565b3480156102e757600080fd5b506102f0610abb565b60405161021891906116b6565b34801561030957600080fd5b5061019661031836600461159b565b610acc565b34801561032957600080fd5b5060085461033d906001600160401b031681565b6040516001600160401b039091168152602001610218565b34801561036157600080fd5b5061020e610370366004611703565b610af8565b34801561038157600080fd5b50610196610390366004611791565b610b9b565b3480156103a157600080fd5b506101966103b03660046117dc565b610bae565b3480156103c157600080fd5b5061020e610bda565b3480156103d657600080fd5b506101966103e53660046117f7565b610be6565b3480156103f657600080fd5b50610262610405366004611606565b610c44565b34801561041657600080fd5b50610262610425366004611606565b610c51565b34801561043657600080fd5b5061033d7f000000000000000000000000000000000000000000000000000000000000000081565b34801561046a57600080fd5b506102f0610c79565b34801561047f57600080fd5b5061020e610c85565b34801561049457600080fd5b506104b960405180604001604052806005815260200164302e302e3160d81b81525081565b6040516102189190611861565b3330146104e957604051600162ab40b560e01b0319815260040160405180910390fd5b80156104fc576104f882610c91565b5050565b6104f882610d2e565b8242111561052657604051634f01675560e01b815260040160405180910390fd5b6040805180820182526002815261190160f01b6020808301919091528251808401845260068152654f6e6553696760d01b90820152825180840184526005815264302e302e3160d81b9082015282517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527fbd7855a8d66a83be54ebc7fa5f2e3fb658ab6007afa89cd93dfca08ad1b97ec8818501527fae209a0b48f21c054280f2455d32cf309387644879d9acbd8ffc19916381188560608201526001608082015261dead60a0808301919091528451808303909101815260c0820185528051908301206007547f642ed5d2b77bc7ccb98e10da4c02d7cd8231228da4222a9f88a80c15545074ed60e084015261010083015261012082018990526101408083018990528551808403909101815261016083019095528451949092019390932060009361067d9392919061018001611894565b6040516020818303038152906040528051906020012090506106a0818484610b9b565b5050505050565b3330146106ca57604051600162ab40b560e01b0319815260040160405180910390fd5b80156106d9576104f882610d9d565b6104f882610e33565b33301461070557604051600162ab40b560e01b0319815260040160405180910390fd5b61070e81610edf565b50565b806000036107325760405163831761d760e01b815260040160405180910390fd5b61073d6041836118d1565b1561075b5760405163669233e360e11b815260040160405180910390fd5b60006107686041846118fb565b90508181101561078b5760405163669233e360e11b815260040160405180910390fd5b6000805b828110156108885736600087876107a760418661190f565b9060416107b5876001611926565b6107bf919061190f565b926107cc93929190611939565b9150915060006108128a84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610f1b92505050565b9050846001600160a01b0316816001600160a01b0316116108465760405163d02ef0e560e01b815260040160405180910390fd5b61084f81610891565b61087c576040516353b1def160e01b81526001600160a01b03821660048201526024015b60405180910390fd5b9350505060010161078f565b50505050505050565b600061089d8183610f45565b92915050565b6108ab610f6a565b6108b433610c51565b6108d15760405163245b1c3160e01b815260040160405180910390fd5b6108dd84848484610505565b6108e78486610be6565b600880546000916001600160401b03909116908261090483611963565b91906101000a8154816001600160401b0302191690836001600160401b031602179055506001600160401b0316905060005b6109408780611989565b9050811015610a765760006109558880611989565b83818110610965576109656119d2565b905060200281019061097791906119e8565b610985906020810190611606565b6001600160a01b03166109988980611989565b848181106109a8576109a86119d2565b90506020028101906109ba91906119e8565b602001356109c88a80611989565b858181106109d8576109d86119d2565b90506020028101906109ea91906119e8565b6109f8906040810190611a08565b604051610a06929190611a4e565b60006040518083038185875af1925050503d8060008114610a43576040519150601f19603f3d011682016040523d82523d6000602084013e610a48565b606091505b5050905080610a6d57604051632497eb8760e11b815260048101839052602401610873565b50600101610936565b5060408051868152602081018390527fd9a0df418868c38712b1f6eab685bc77a2e49596edd77667ab34d5448f5f941c910160405180910390a1506106a06001600355565b6060610ac76000610f94565b905090565b333014610aef57604051600162ab40b560e01b0319815260040160405180910390fd5b61070e81610fa1565b600060017f0000000000000000000000000000000000000000000000000000000000000000306001600160a01b031660001b868686604051602001610b3e929190611a5e565b60408051601f1981840301815290829052610b5f9594939291602001611b4e565b60408051601f19818403018152828252805160209182012090830152016040516020818303038152906040528051906020012090509392505050565b610ba9838383600254610711565b505050565b333014610bd157604051600162ab40b560e01b0319815260040160405180910390fd5b61070e81611031565b6000610ac76000611072565b600854600090610c03906001600160401b03166103708480611989565b90506000610c1e610c176020850185611989565b868561107c565b905080610c3e5760405163150b827160e11b815260040160405180910390fd5b50505050565b600061089d600483610f45565b60065460009060ff161580610c6a5750610c6a82610c44565b8061089d575061089d82610891565b6060610ac76004610f94565b6000610ac76004611072565b6001600160a01b038116610cb85760405163710c949760e01b815260040160405180910390fd5b610cc3600482611094565b610ceb5760405163c06cd70160e01b81526001600160a01b0382166004820152602401610873565b604051600181526001600160a01b038216907f278b09622564dd3991fe7744514513d64ea2c8ed2b2b9ec1150ad964fde80a99906020015b60405180910390a250565b610d396004826110a9565b610d61576040516302cf3fb360e51b81526001600160a01b0382166004820152602401610873565b604051600081526001600160a01b038216907f278b09622564dd3991fe7744514513d64ea2c8ed2b2b9ec1150ad964fde80a9990602001610d23565b6001600160a01b038116610dc457604051632057875960e21b815260040160405180910390fd5b610dcf600082611094565b610df7576040516327ec359360e21b81526001600160a01b0382166004820152602401610873565b604051600181526001600160a01b038216907ffc4acb499491cd850a8a21ab98c7f128850c0f0e5f1a875a62b7fa055c2ecf1990602001610d23565b610e3e6000826110a9565b610e66576040516353b1def160e01b81526001600160a01b0382166004820152602401610873565b600254610e71610bda565b1015610ea357610e7f610bda565b60025460405162daa43760e71b815260048101929092526024820152604401610873565b604051600081526001600160a01b038216907ffc4acb499491cd850a8a21ab98c7f128850c0f0e5f1a875a62b7fa055c2ecf1990602001610d23565b60078190556040518181527f36d9c0a688d366a03517cca4843dfff4e45db3869d5fd86fb9d15a1b45ce0e5a906020015b60405180910390a150565b600080600080610f2b86866110be565b925092509250610f3b828261110b565b5090949350505050565b6001600160a01b038116600090815260018301602052604081205415155b9392505050565b600260035403610f8d57604051633ee5aeb560e01b815260040160405180910390fd5b6002600355565b60606000610f63836111c4565b80600003610fc25760405163831761d760e01b815260040160405180910390fd5b80610fcb610bda565b1015610ffc57610fd9610bda565b60405162daa43760e71b8152600481019190915260248101829052604401610873565b60028190556040518181527f6e8a187d7944998085dbd1f16b84c51c903bb727536cdba86962439aded2cfd790602001610f10565b6006805460ff19168215159081179091556040519081527faf318623f8b8327d379709bc2f6846f21be20c305509acd41dccf7e07a48534790602001610f10565b600061089d825490565b60008261108a868685611220565b1495945050505050565b6000610f63836001600160a01b038416611262565b6000610f63836001600160a01b0384166112b1565b600080600083516041036110f85760208401516040850151606086015160001a6110ea888285856113a4565b955095509550505050611104565b50508151600091506002905b9250925092565b600082600381111561111f5761111f611ba8565b03611128575050565b600182600381111561113c5761113c611ba8565b0361115a5760405163f645eedf60e01b815260040160405180910390fd5b600282600381111561116e5761116e611ba8565b0361118f5760405163fce698f760e01b815260048101829052602401610873565b60038260038111156111a3576111a3611ba8565b036104f8576040516335e2f38360e21b815260048101829052602401610873565b60608160000180548060200260200160405190810160405280929190818152602001828054801561121457602002820191906000526020600020905b815481526020019060010190808311611200575b50505050509050919050565b600081815b848110156112595761124f82878784818110611243576112436119d2565b90506020020135611473565b9150600101611225565b50949350505050565b60008181526001830160205260408120546112a95750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561089d565b50600061089d565b6000818152600183016020526040812054801561139a5760006112d5600183611bbe565b85549091506000906112e990600190611bbe565b905080821461134e576000866000018281548110611309576113096119d2565b906000526020600020015490508087600001848154811061132c5761132c6119d2565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061135f5761135f611bd1565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061089d565b600091505061089d565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411156113df5750600091506003905082611469565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611433573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661145f57506000925060019150829050611469565b9250600091508190505b9450945094915050565b600081831061148f576000828152602084905260409020610f63565b6000838152602083905260409020610f63565b80356001600160a01b03811681146114b957600080fd5b919050565b803580151581146114b957600080fd5b600080604083850312156114e157600080fd5b6114ea836114a2565b91506114f8602084016114be565b90509250929050565b60008083601f84011261151357600080fd5b5081356001600160401b0381111561152a57600080fd5b60208301915083602082850101111561154257600080fd5b9250929050565b6000806000806060858703121561155f57600080fd5b843593506020850135925060408501356001600160401b0381111561158357600080fd5b61158f87828801611501565b95989497509550505050565b6000602082840312156115ad57600080fd5b5035919050565b600080600080606085870312156115ca57600080fd5b8435935060208501356001600160401b038111156115e757600080fd5b6115f387828801611501565b9598909750949560400135949350505050565b60006020828403121561161857600080fd5b610f63826114a2565b60006040828403121561163357600080fd5b50919050565b60008060008060006080868803121561165157600080fd5b85356001600160401b038082111561166857600080fd5b61167489838a01611621565b96506020880135955060408801359450606088013591508082111561169857600080fd5b506116a588828901611501565b969995985093965092949392505050565b6020808252825182820181905260009190848201906040850190845b818110156116f75783516001600160a01b0316835292840192918401916001016116d2565b50909695505050505050565b60008060006040848603121561171857600080fd5b83356001600160401b03808216821461173057600080fd5b9093506020850135908082111561174657600080fd5b818601915086601f83011261175a57600080fd5b81358181111561176957600080fd5b8760208260051b850101111561177e57600080fd5b6020830194508093505050509250925092565b6000806000604084860312156117a657600080fd5b8335925060208401356001600160401b038111156117c357600080fd5b6117cf86828701611501565b9497909650939450505050565b6000602082840312156117ee57600080fd5b610f63826114be565b6000806040838503121561180a57600080fd5b8235915060208301356001600160401b0381111561182757600080fd5b61183385828601611621565b9150509250929050565b60005b83811015611858578181015183820152602001611840565b50506000910152565b602081526000825180602084015261188081604085016020870161183d565b601f01601f19169190910160400192915050565b600084516118a681846020890161183d565b91909101928352506020820152604001919050565b634e487b7160e01b600052601260045260246000fd5b6000826118e0576118e06118bb565b500690565b634e487b7160e01b600052601160045260246000fd5b60008261190a5761190a6118bb565b500490565b808202811582820484141761089d5761089d6118e5565b8082018082111561089d5761089d6118e5565b6000808585111561194957600080fd5b8386111561195657600080fd5b5050820193919092039150565b60006001600160401b0380831681810361197f5761197f6118e5565b6001019392505050565b6000808335601e198436030181126119a057600080fd5b8301803591506001600160401b038211156119ba57600080fd5b6020019150600581901b360382131561154257600080fd5b634e487b7160e01b600052603260045260246000fd5b60008235605e198336030181126119fe57600080fd5b9190910192915050565b6000808335601e19843603018112611a1f57600080fd5b8301803591506001600160401b03821115611a3957600080fd5b60200191503681900382131561154257600080fd5b8183823760009101908152919050565b60208082528181018390526000906040808401600586901b8501820187855b88811015611b4057878303603f190184528135368b9003605e19018112611aa357600080fd5b8a0160606001600160a01b03611ab8836114a2565b168552878201358886015286820135601e19833603018112611ad957600080fd5b9091018781019190356001600160401b03811115611af657600080fd5b803603831315611b0557600080fd5b8188870152808287015260809150808383880137600086820183015295880195601f01601f1916909401909301925090850190600101611a7d565b509098975050505050505050565b60ff60f81b8660f81b16815260006001600160401b0360c01b808760c01b166001840152856009840152808560c01b166029840152508251611b9781603185016020870161183d565b919091016031019695505050505050565b634e487b7160e01b600052602160045260246000fd5b8181038181111561089d5761089d6118e5565b634e487b7160e01b600052603160045260246000fdfea26469706673582212209946885046aa71e95b82d42220073304909ffb06ef88e120984dfeb032c709c764736f6c6343000816003300000000000000000000000000000000000000000000000000000000000076a700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001a8c6099081c03ac4c11ed511690f296077e94d44cf99bebc8c349cf972f8734000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000cb72c1f6a36c225a7e2b21712e8853a4a1acc470000000000000000000000005bc6aa6ad117a8b50abf9e1658971f5da1968c5c00000000000000000000000073e9c017ad37e2113e709d8070cc9e1b28180e1e000000000000000000000000771dcacb96024d1e55fd21fe8a8187aa7ec9e77e000000000000000000000000e67db04d7eff4e9ec282ed929632d4ff058112d7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000039f86ecef62c5bce23428d6b7c7050d9ecb0e346

Deployed Bytecode

0x60806040526004361061016a5760003560e01c8063960bfe04116100d1578063d63f97c91161008a578063e9ec3f5811610064578063e9ec3f581461042a578063ef09e78f1461045e578063f621ff6014610473578063ffa1ad741461048857600080fd5b8063d63f97c9146103ca578063debfda30146103ea578063e1ed81761461040a57600080fd5b8063960bfe04146102fd578063affed0e01461031d578063b677cde414610355578063c7a823e014610375578063cf734e8a14610395578063d1a62648146103b557600080fd5b8063606c22e011610123578063606c22e0146102485780636419ebde146102725780637d94792a146102925780637df73e27146102a85780638e6725bd146102c857806394cf795e146102db57600080fd5b80631e1bff3f146101765780632b03f6ab1461019857806331cb6105146101b8578063347024ff146101d857806342cde4e8146101f857806349dcfdd51461022157600080fd5b3661017157005b600080fd5b34801561018257600080fd5b506101966101913660046114ce565b6104c6565b005b3480156101a457600080fd5b506101966101b3366004611549565b610505565b3480156101c457600080fd5b506101966101d33660046114ce565b6106a7565b3480156101e457600080fd5b506101966101f336600461159b565b6106e2565b34801561020457600080fd5b5061020e60025481565b6040519081526020015b60405180910390f35b34801561022d57600080fd5b50610236600181565b60405160ff9091168152602001610218565b34801561025457600080fd5b506006546102629060ff1681565b6040519015158152602001610218565b34801561027e57600080fd5b5061019661028d3660046115b4565b610711565b34801561029e57600080fd5b5061020e60075481565b3480156102b457600080fd5b506102626102c3366004611606565b610891565b6101966102d6366004611639565b6108a3565b3480156102e757600080fd5b506102f0610abb565b60405161021891906116b6565b34801561030957600080fd5b5061019661031836600461159b565b610acc565b34801561032957600080fd5b5060085461033d906001600160401b031681565b6040516001600160401b039091168152602001610218565b34801561036157600080fd5b5061020e610370366004611703565b610af8565b34801561038157600080fd5b50610196610390366004611791565b610b9b565b3480156103a157600080fd5b506101966103b03660046117dc565b610bae565b3480156103c157600080fd5b5061020e610bda565b3480156103d657600080fd5b506101966103e53660046117f7565b610be6565b3480156103f657600080fd5b50610262610405366004611606565b610c44565b34801561041657600080fd5b50610262610425366004611606565b610c51565b34801561043657600080fd5b5061033d7f00000000000000000000000000000000000000000000000000000000000076a781565b34801561046a57600080fd5b506102f0610c79565b34801561047f57600080fd5b5061020e610c85565b34801561049457600080fd5b506104b960405180604001604052806005815260200164302e302e3160d81b81525081565b6040516102189190611861565b3330146104e957604051600162ab40b560e01b0319815260040160405180910390fd5b80156104fc576104f882610c91565b5050565b6104f882610d2e565b8242111561052657604051634f01675560e01b815260040160405180910390fd5b6040805180820182526002815261190160f01b6020808301919091528251808401845260068152654f6e6553696760d01b90820152825180840184526005815264302e302e3160d81b9082015282517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527fbd7855a8d66a83be54ebc7fa5f2e3fb658ab6007afa89cd93dfca08ad1b97ec8818501527fae209a0b48f21c054280f2455d32cf309387644879d9acbd8ffc19916381188560608201526001608082015261dead60a0808301919091528451808303909101815260c0820185528051908301206007547f642ed5d2b77bc7ccb98e10da4c02d7cd8231228da4222a9f88a80c15545074ed60e084015261010083015261012082018990526101408083018990528551808403909101815261016083019095528451949092019390932060009361067d9392919061018001611894565b6040516020818303038152906040528051906020012090506106a0818484610b9b565b5050505050565b3330146106ca57604051600162ab40b560e01b0319815260040160405180910390fd5b80156106d9576104f882610d9d565b6104f882610e33565b33301461070557604051600162ab40b560e01b0319815260040160405180910390fd5b61070e81610edf565b50565b806000036107325760405163831761d760e01b815260040160405180910390fd5b61073d6041836118d1565b1561075b5760405163669233e360e11b815260040160405180910390fd5b60006107686041846118fb565b90508181101561078b5760405163669233e360e11b815260040160405180910390fd5b6000805b828110156108885736600087876107a760418661190f565b9060416107b5876001611926565b6107bf919061190f565b926107cc93929190611939565b9150915060006108128a84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610f1b92505050565b9050846001600160a01b0316816001600160a01b0316116108465760405163d02ef0e560e01b815260040160405180910390fd5b61084f81610891565b61087c576040516353b1def160e01b81526001600160a01b03821660048201526024015b60405180910390fd5b9350505060010161078f565b50505050505050565b600061089d8183610f45565b92915050565b6108ab610f6a565b6108b433610c51565b6108d15760405163245b1c3160e01b815260040160405180910390fd5b6108dd84848484610505565b6108e78486610be6565b600880546000916001600160401b03909116908261090483611963565b91906101000a8154816001600160401b0302191690836001600160401b031602179055506001600160401b0316905060005b6109408780611989565b9050811015610a765760006109558880611989565b83818110610965576109656119d2565b905060200281019061097791906119e8565b610985906020810190611606565b6001600160a01b03166109988980611989565b848181106109a8576109a86119d2565b90506020028101906109ba91906119e8565b602001356109c88a80611989565b858181106109d8576109d86119d2565b90506020028101906109ea91906119e8565b6109f8906040810190611a08565b604051610a06929190611a4e565b60006040518083038185875af1925050503d8060008114610a43576040519150601f19603f3d011682016040523d82523d6000602084013e610a48565b606091505b5050905080610a6d57604051632497eb8760e11b815260048101839052602401610873565b50600101610936565b5060408051868152602081018390527fd9a0df418868c38712b1f6eab685bc77a2e49596edd77667ab34d5448f5f941c910160405180910390a1506106a06001600355565b6060610ac76000610f94565b905090565b333014610aef57604051600162ab40b560e01b0319815260040160405180910390fd5b61070e81610fa1565b600060017f00000000000000000000000000000000000000000000000000000000000076a7306001600160a01b031660001b868686604051602001610b3e929190611a5e565b60408051601f1981840301815290829052610b5f9594939291602001611b4e565b60408051601f19818403018152828252805160209182012090830152016040516020818303038152906040528051906020012090509392505050565b610ba9838383600254610711565b505050565b333014610bd157604051600162ab40b560e01b0319815260040160405180910390fd5b61070e81611031565b6000610ac76000611072565b600854600090610c03906001600160401b03166103708480611989565b90506000610c1e610c176020850185611989565b868561107c565b905080610c3e5760405163150b827160e11b815260040160405180910390fd5b50505050565b600061089d600483610f45565b60065460009060ff161580610c6a5750610c6a82610c44565b8061089d575061089d82610891565b6060610ac76004610f94565b6000610ac76004611072565b6001600160a01b038116610cb85760405163710c949760e01b815260040160405180910390fd5b610cc3600482611094565b610ceb5760405163c06cd70160e01b81526001600160a01b0382166004820152602401610873565b604051600181526001600160a01b038216907f278b09622564dd3991fe7744514513d64ea2c8ed2b2b9ec1150ad964fde80a99906020015b60405180910390a250565b610d396004826110a9565b610d61576040516302cf3fb360e51b81526001600160a01b0382166004820152602401610873565b604051600081526001600160a01b038216907f278b09622564dd3991fe7744514513d64ea2c8ed2b2b9ec1150ad964fde80a9990602001610d23565b6001600160a01b038116610dc457604051632057875960e21b815260040160405180910390fd5b610dcf600082611094565b610df7576040516327ec359360e21b81526001600160a01b0382166004820152602401610873565b604051600181526001600160a01b038216907ffc4acb499491cd850a8a21ab98c7f128850c0f0e5f1a875a62b7fa055c2ecf1990602001610d23565b610e3e6000826110a9565b610e66576040516353b1def160e01b81526001600160a01b0382166004820152602401610873565b600254610e71610bda565b1015610ea357610e7f610bda565b60025460405162daa43760e71b815260048101929092526024820152604401610873565b604051600081526001600160a01b038216907ffc4acb499491cd850a8a21ab98c7f128850c0f0e5f1a875a62b7fa055c2ecf1990602001610d23565b60078190556040518181527f36d9c0a688d366a03517cca4843dfff4e45db3869d5fd86fb9d15a1b45ce0e5a906020015b60405180910390a150565b600080600080610f2b86866110be565b925092509250610f3b828261110b565b5090949350505050565b6001600160a01b038116600090815260018301602052604081205415155b9392505050565b600260035403610f8d57604051633ee5aeb560e01b815260040160405180910390fd5b6002600355565b60606000610f63836111c4565b80600003610fc25760405163831761d760e01b815260040160405180910390fd5b80610fcb610bda565b1015610ffc57610fd9610bda565b60405162daa43760e71b8152600481019190915260248101829052604401610873565b60028190556040518181527f6e8a187d7944998085dbd1f16b84c51c903bb727536cdba86962439aded2cfd790602001610f10565b6006805460ff19168215159081179091556040519081527faf318623f8b8327d379709bc2f6846f21be20c305509acd41dccf7e07a48534790602001610f10565b600061089d825490565b60008261108a868685611220565b1495945050505050565b6000610f63836001600160a01b038416611262565b6000610f63836001600160a01b0384166112b1565b600080600083516041036110f85760208401516040850151606086015160001a6110ea888285856113a4565b955095509550505050611104565b50508151600091506002905b9250925092565b600082600381111561111f5761111f611ba8565b03611128575050565b600182600381111561113c5761113c611ba8565b0361115a5760405163f645eedf60e01b815260040160405180910390fd5b600282600381111561116e5761116e611ba8565b0361118f5760405163fce698f760e01b815260048101829052602401610873565b60038260038111156111a3576111a3611ba8565b036104f8576040516335e2f38360e21b815260048101829052602401610873565b60608160000180548060200260200160405190810160405280929190818152602001828054801561121457602002820191906000526020600020905b815481526020019060010190808311611200575b50505050509050919050565b600081815b848110156112595761124f82878784818110611243576112436119d2565b90506020020135611473565b9150600101611225565b50949350505050565b60008181526001830160205260408120546112a95750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561089d565b50600061089d565b6000818152600183016020526040812054801561139a5760006112d5600183611bbe565b85549091506000906112e990600190611bbe565b905080821461134e576000866000018281548110611309576113096119d2565b906000526020600020015490508087600001848154811061132c5761132c6119d2565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061135f5761135f611bd1565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061089d565b600091505061089d565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411156113df5750600091506003905082611469565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611433573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661145f57506000925060019150829050611469565b9250600091508190505b9450945094915050565b600081831061148f576000828152602084905260409020610f63565b6000838152602083905260409020610f63565b80356001600160a01b03811681146114b957600080fd5b919050565b803580151581146114b957600080fd5b600080604083850312156114e157600080fd5b6114ea836114a2565b91506114f8602084016114be565b90509250929050565b60008083601f84011261151357600080fd5b5081356001600160401b0381111561152a57600080fd5b60208301915083602082850101111561154257600080fd5b9250929050565b6000806000806060858703121561155f57600080fd5b843593506020850135925060408501356001600160401b0381111561158357600080fd5b61158f87828801611501565b95989497509550505050565b6000602082840312156115ad57600080fd5b5035919050565b600080600080606085870312156115ca57600080fd5b8435935060208501356001600160401b038111156115e757600080fd5b6115f387828801611501565b9598909750949560400135949350505050565b60006020828403121561161857600080fd5b610f63826114a2565b60006040828403121561163357600080fd5b50919050565b60008060008060006080868803121561165157600080fd5b85356001600160401b038082111561166857600080fd5b61167489838a01611621565b96506020880135955060408801359450606088013591508082111561169857600080fd5b506116a588828901611501565b969995985093965092949392505050565b6020808252825182820181905260009190848201906040850190845b818110156116f75783516001600160a01b0316835292840192918401916001016116d2565b50909695505050505050565b60008060006040848603121561171857600080fd5b83356001600160401b03808216821461173057600080fd5b9093506020850135908082111561174657600080fd5b818601915086601f83011261175a57600080fd5b81358181111561176957600080fd5b8760208260051b850101111561177e57600080fd5b6020830194508093505050509250925092565b6000806000604084860312156117a657600080fd5b8335925060208401356001600160401b038111156117c357600080fd5b6117cf86828701611501565b9497909650939450505050565b6000602082840312156117ee57600080fd5b610f63826114be565b6000806040838503121561180a57600080fd5b8235915060208301356001600160401b0381111561182757600080fd5b61183385828601611621565b9150509250929050565b60005b83811015611858578181015183820152602001611840565b50506000910152565b602081526000825180602084015261188081604085016020870161183d565b601f01601f19169190910160400192915050565b600084516118a681846020890161183d565b91909101928352506020820152604001919050565b634e487b7160e01b600052601260045260246000fd5b6000826118e0576118e06118bb565b500690565b634e487b7160e01b600052601160045260246000fd5b60008261190a5761190a6118bb565b500490565b808202811582820484141761089d5761089d6118e5565b8082018082111561089d5761089d6118e5565b6000808585111561194957600080fd5b8386111561195657600080fd5b5050820193919092039150565b60006001600160401b0380831681810361197f5761197f6118e5565b6001019392505050565b6000808335601e198436030181126119a057600080fd5b8301803591506001600160401b038211156119ba57600080fd5b6020019150600581901b360382131561154257600080fd5b634e487b7160e01b600052603260045260246000fd5b60008235605e198336030181126119fe57600080fd5b9190910192915050565b6000808335601e19843603018112611a1f57600080fd5b8301803591506001600160401b03821115611a3957600080fd5b60200191503681900382131561154257600080fd5b8183823760009101908152919050565b60208082528181018390526000906040808401600586901b8501820187855b88811015611b4057878303603f190184528135368b9003605e19018112611aa357600080fd5b8a0160606001600160a01b03611ab8836114a2565b168552878201358886015286820135601e19833603018112611ad957600080fd5b9091018781019190356001600160401b03811115611af657600080fd5b803603831315611b0557600080fd5b8188870152808287015260809150808383880137600086820183015295880195601f01601f1916909401909301925090850190600101611a7d565b509098975050505050505050565b60ff60f81b8660f81b16815260006001600160401b0360c01b808760c01b166001840152856009840152808560c01b166029840152508251611b9781603185016020870161183d565b919091016031019695505050505050565b634e487b7160e01b600052602160045260246000fd5b8181038181111561089d5761089d6118e5565b634e487b7160e01b600052603160045260246000fdfea26469706673582212209946885046aa71e95b82d42220073304909ffb06ef88e120984dfeb032c709c764736f6c63430008160033

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

00000000000000000000000000000000000000000000000000000000000076a700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001a8c6099081c03ac4c11ed511690f296077e94d44cf99bebc8c349cf972f8734000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000cb72c1f6a36c225a7e2b21712e8853a4a1acc470000000000000000000000005bc6aa6ad117a8b50abf9e1658971f5da1968c5c00000000000000000000000073e9c017ad37e2113e709d8070cc9e1b28180e1e000000000000000000000000771dcacb96024d1e55fd21fe8a8187aa7ec9e77e000000000000000000000000e67db04d7eff4e9ec282ed929632d4ff058112d7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000039f86ecef62c5bce23428d6b7c7050d9ecb0e346

-----Decoded View---------------
Arg [0] : _oneSigId (uint64): 30375
Arg [1] : _signers (address[]): 0x0cb72C1F6a36c225A7E2B21712E8853A4A1acc47,0x5bC6AA6ad117A8B50ABf9E1658971f5DA1968c5c,0x73E9c017Ad37e2113e709D8070Cc9E1b28180e1e,0x771dcAcB96024d1e55Fd21Fe8a8187AA7EC9e77e,0xe67DB04d7eFF4e9ec282eD929632D4FF058112d7
Arg [2] : _threshold (uint256): 3
Arg [3] : _executors (address[]): 0x39f86ECef62c5bcE23428d6b7c7050D9Ecb0e346
Arg [4] : _executorRequired (bool): True
Arg [5] : _seed (bytes32): 0xa8c6099081c03ac4c11ed511690f296077e94d44cf99bebc8c349cf972f87340

-----Encoded View---------------
14 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000076a7
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [5] : a8c6099081c03ac4c11ed511690f296077e94d44cf99bebc8c349cf972f87340
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [7] : 0000000000000000000000000cb72c1f6a36c225a7e2b21712e8853a4a1acc47
Arg [8] : 0000000000000000000000005bc6aa6ad117a8b50abf9e1658971f5da1968c5c
Arg [9] : 00000000000000000000000073e9c017ad37e2113e709d8070cc9e1b28180e1e
Arg [10] : 000000000000000000000000771dcacb96024d1e55fd21fe8a8187aa7ec9e77e
Arg [11] : 000000000000000000000000e67db04d7eff4e9ec282ed929632d4ff058112d7
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [13] : 00000000000000000000000039f86ecef62c5bce23428d6b7c7050d9ecb0e346


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
0xE590a6730D7a8790E99ce3db11466Acb644c3942
Loading...
Loading
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.