Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0xD2e28229F6f2c235e57De2EbC727025A1D0530FB
Balance 0 ETH
Nonce 1
Code Size 6905 bytes
Indexed Transactions 0 (1 on-chain, 1.6% indexed)
External Etherscan · Sourcify

Contract Bytecode

6905 bytes
Copy Bytecode
0x60806040526004361015610010575b005b5f3560e01c806223de291461011e57806301ffc9a714610119578063150b7a02146101145780631626ba7e1461010f57806319822f7c1461010a5780631d92e4b61461010557806326da7d881461010057806334fcd5be146100fb57806376276c82146100f65780638626e88b146100f15780639cfd7cff146100ec578063a4c0ed36146100e7578063b0d691fe146100e2578063b61d27f6146100dd578063bc197c81146100d8578063cf21ecab146100d35763f23a6e610361000e57611049565b610fdb565b610f05565b610ee2565b610e9a565b610e7d565b610dcb565b610cef565b610c1a565b610b7f565b610a4f565b610831565b61064b565b6103f5565b610364565b610205565b610173565b73ffffffffffffffffffffffffffffffffffffffff81160361014157565b5f80fd5b9181601f840112156101415782359167ffffffffffffffff8311610141576020838186019501011161014157565b346101415760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610141576101ad600435610123565b6101b8602435610123565b6101c3604435610123565b60843567ffffffffffffffff8111610141576101e3903690600401610145565b505060a43567ffffffffffffffff81116101415761000e903690600401610145565b346101415760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610141576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361014157807f4e2312e0000000000000000000000000000000000000000000000000000000006102b8921490811561033a575b8115610310575b81156102e6575b81156102bc575b5060405190151581529081906020820190565b0390f35b7f1626ba7e000000000000000000000000000000000000000000000000000000009150145f6102a5565b7f1adb9474000000000000000000000000000000000000000000000000000000008114915061029e565b7f01ffc9a70000000000000000000000000000000000000000000000000000000081149150610297565b7f150b7a020000000000000000000000000000000000000000000000000000000081149150610290565b346101415760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101415761039e600435610123565b6103a9602435610123565b60643567ffffffffffffffff8111610141576103c9903690600401610145565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b346101415760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101415760243560043567ffffffffffffffff82116101415761044a6105c5923690600401610145565b9160405160208101917f31322a37c2a66b24e1088197e5b24fcc050625c13d4b84c3eaa6a8be5270321d835260408201526040815261048a606082611107565b5190206105916105bd60405160208101907fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647282527f906754a21e6afe7233c7ccce2787110e84157e1cbaa80e58656dff779193a5cd60408201527f15124d26d1272f8d4d5266a24ca397811f414b8cd05a53b26b745f63af5ae2fc60608201524660808201523060a08201527f000000000000000000000000d2e28229f6f2c235e57de2ebc727025a1d0530fb60c082015260c0815261054b60e082611107565b5190209260405192839160208301958690916042927f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201520190565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611107565b519020611827565b15610622576102b87f1626ba7e000000000000000000000000000000000000000000000000000000005b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681529081906020820190565b6102b87fffffffff000000000000000000000000000000000000000000000000000000006105ef565b346101415760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101415760043567ffffffffffffffff8111610141576101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc823603011261014157602435906044356f71727de22e5e9d8baf0edac6f37da03233036107d8576040517f7e97025600000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006067203e59e681396335940c4f2caf6b002815bc165afa9081156107d3575f91610798575b5015610770576102b8926107609260040161121d565b6040519081529081906020820190565b7fd13d7835000000000000000000000000000000000000000000000000000000005f5260045ffd5b90506020813d6020116107cb575b816107b360209383611107565b8101031261014157518015158103610141575f61074a565b3d91506107a6565b61114d565b7fbd07c551000000000000000000000000000000000000000000000000000000005f5260045ffd5b9181601f840112156101415782359167ffffffffffffffff8311610141576020808501948460051b01011161014157565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101415760043567ffffffffffffffff81116101415761087b903690600401610800565b9060243567ffffffffffffffff81116101415761089c903690600401610145565b90604051917f627cdcb90000000000000000000000000000000000000000000000000000000083526020836004815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006067203e59e681396335940c4f2caf6b002815bc165af19182156107d35761093d61094293610946955f916109d3575b506040516109358161059160208201948c8c876114ae565b519020611721565b611827565b1590565b6109ab575f5b82811061095557005b806109a561096e61096960019487876115e7565b611654565b602061097b8488886115e7565b013561099f61099861098e868a8a6115e7565b6040810190611158565b369161169b565b916118ad565b0161094c565b7f8baa579f000000000000000000000000000000000000000000000000000000005f5260045ffd5b6109f5915060203d6020116109fb575b6109ed8183611107565b810190611461565b5f61091d565b503d6109e3565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610141576004359067ffffffffffffffff821161014157610a4b91600401610800565b9091565b610a5836610a02565b6f71727de22e5e9d8baf0edac6f37da03233036107d8575f917fa77908bfab24e6768503125e99551a4fa5c3c75f3106fee5dea5ac0cebc017a45c9182159182610b23575b90610aa7916116d1565b15610ad9575b50610ab457005b5f7fa77908bfab24e6768503125e99551a4fa5c3c75f3106fee5dea5ac0cebc017a455005b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017fa77908bfab24e6768503125e99551a4fa5c3c75f3106fee5dea5ac0cebc017a45d5f610aad565b9350611a857fa77908bfab24e6768503125e99551a4fa5c3c75f3106fee5dea5ac0cebc017a45403610b5757600193610a9d565b7f33d7b3d6000000000000000000000000000000000000000000000000000000005f5260045ffd5b610b8836610a02565b303303610b985761000e916116d1565b7f14d4a4e8000000000000000000000000000000000000000000000000000000005f5260045ffd5b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82011261014157600435610bf681610123565b91602435916044359067ffffffffffffffff821161014157610a4b91600401610145565b610c2336610bc0565b9091926f71727de22e5e9d8baf0edac6f37da03233036107d8575f937fa77908bfab24e6768503125e99551a4fa5c3c75f3106fee5dea5ac0cebc017a45c9384159384610c7c575b610aa793929161099f91369161169b565b95509190611a857fa77908bfab24e6768503125e99551a4fa5c3c75f3106fee5dea5ac0cebc017a45403610b575760019591929091610c6b565b60206040818301928281528451809452019201905f5b818110610cd95750505090565b8251845260209384019390920191600101610ccc565b34610141575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610141576040518060207fc473de86d0138e06e4d4918a106463a7cc005258d2e21915272bcb4594c18900549182815201907fc473de86d0138e06e4d4918a106463a7cc005258d2e21915272bcb4594c189005f527fdb3901c0862740f848a4aebebd6b3d645e6b3bc6adf2cf46e83a846b3a343933905f5b818110610db5576102b885610da981870382611107565b60405191829182610cb6565b8254845260209093019260019283019201610d92565b34610141575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610141576040805190610e088183611107565b6016825260208201917f547275737457616c6c65742e42697a2e76312e302e300000000000000000000083527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8351948593602085525180918160208701528686015e5f85828601015201168101030190f35b3461014157610e8b36610bc0565b50505050602060405160018152f35b34610141575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101415760206040516f71727de22e5e9d8baf0edac6f37da0328152f35b610eeb36610bc0565b92919092303303610b985761000e9361099f91369161169b565b346101415760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014157610f3f600435610123565b610f4a602435610123565b60443567ffffffffffffffff811161014157610f6a903690600401610800565b505060643567ffffffffffffffff811161014157610f8c903690600401610800565b505060843567ffffffffffffffff811161014157610fae903690600401610145565b50506040517fbc197c81000000000000000000000000000000000000000000000000000000008152602090f35b34610141575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014157602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006067203e59e681396335940c4f2caf6b002815bc168152f35b346101415760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014157611083600435610123565b61108e602435610123565b60843567ffffffffffffffff8111610141576110ae903690600401610145565b505060206040517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761114857604052565b6110da565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610141570180359067ffffffffffffffff82116101415760200191813603831361014157565b906004116101415790600490565b919091357fffffffff00000000000000000000000000000000000000000000000000000000811692600481106111eb575050565b7fffffffff00000000000000000000000000000000000000000000000000000000929350829060040360031b1b161690565b9061133c9093929360405160208101917f4f51e7a567f083a31264743067875fc6a7ae45c32c5bd71f6a998c4625b138678352604082015260408152611264606082611107565b51902061059161132560405160208101907fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647282527f906754a21e6afe7233c7ccce2787110e84157e1cbaa80e58656dff779193a5cd60408201527f15124d26d1272f8d4d5266a24ca397811f414b8cd05a53b26b745f63af5ae2fc60608201524660808201523060a08201527f000000000000000000000000d2e28229f6f2c235e57de2ebc727025a1d0530fb60c082015260c0815261054b60e082611107565b519020611336610100840184611158565b91611827565b90606081017f76276c82000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000061139f6113996113938587611158565b906111a9565b906111b7565b1614159182611403575b50506113db57156113d2576113bc611864565b815f925b6113c75750565b5f80808093335af150565b816001926113c0565b7f568becaf000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f26da7d880000000000000000000000000000000000000000000000000000000092506113996113937fffffffff000000000000000000000000000000000000000000000000000000009361145793611158565b1614155f806113a9565b90816020910312610141575190565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b93929180604086016040875252606085019060608160051b87010191835f907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1813603015b84831061150857505050505060209150930152565b90919293947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08a820301855286358281121561014157830173ffffffffffffffffffffffffffffffffffffffff813561156081610123565b1682526020810135602083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561014157016020813591019067ffffffffffffffff8111610141578036038213610141576115d8602092839260608681604060019901520191611470565b980197969501930191906114f3565b91908110156116275760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa181360301821215610141570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b3561165e81610123565b90565b67ffffffffffffffff811161114857601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926116a782611661565b916116b56040519384611107565b829481845281830111610141578281602093845f960137010152565b905f5b8181106116e057505050565b8061171b6116f160019385876115e7565b356116fb81610123565b60206117088487896115e7565b013561099f61099861098e86898b6115e7565b016116d4565b60405160208101917fec429430bbd6d0e373848272230d6fe2bac6319d903762e089c5cae97af53df08352604082015260408152611760606082611107565b51902061059161182160405160208101907fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647282527f906754a21e6afe7233c7ccce2787110e84157e1cbaa80e58656dff779193a5cd60408201527f15124d26d1272f8d4d5266a24ca397811f414b8cd05a53b26b745f63af5ae2fc60608201524660808201523060a08201527f000000000000000000000000d2e28229f6f2c235e57de2ebc727025a1d0530fb60c082015260c0815261054b60e082611107565b51902090565b6118559061184f61185e9373ffffffffffffffffffffffffffffffffffffffff95369161169b565b906118fc565b9092919261196d565b16301490565b60017fa77908bfab24e6768503125e99551a4fa5c3c75f3106fee5dea5ac0cebc017a45c017fa77908bfab24e6768503125e99551a4fa5c3c75f3106fee5dea5ac0cebc017a45d565b915f928392602083519301915af13d156118f4573d906118cc82611661565b916118da6040519384611107565b82523d5f602084013e5b156118ec5750565b602081519101fd5b6060906118e4565b815191906041830361192c576119259250602082015190606060408401519301515f1a90611a34565b9192909190565b50505f9160029190565b6004111561194057565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b61197681611936565b8061197f575050565b61198881611936565b600181036119b8577ff645eedf000000000000000000000000000000000000000000000000000000005f5260045ffd5b6119c181611936565b600281036119f557507ffce698f7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b80611a01600392611936565b14611a095750565b7fd78bce0c000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411611ab8579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa156107d3575f5173ffffffffffffffffffffffffffffffffffffffff811615611aae57905f905f90565b505f906001905f90565b5050505f916003919056fea2646970667358221220c2db6d5abc3511f1c5db8e181b811a52e48e4f0b1fdfee2a9ecbcff3bfb11f5764736f6c634300081c0033

Verified Source Code Full Match

Compiler: v0.8.28+commit.7893614a EVM: cancun Optimization: Yes (999999 runs)
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
Ownable2Step.sol 67 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

import {Ownable} from "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This extension of the {Ownable} contract includes a two-step mechanism to transfer
 * ownership, where the new owner must call {acceptOwnership} in order to replace the
 * old one. This can help prevent common mistakes, such as transfers of ownership to
 * incorrect accounts, or to contracts that are unable to interact with the
 * permission system.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     *
     * Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
ECDSA.sol 180 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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 recovered, RecoverError err, bytes32 errArg) {
        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.
            assembly ("memory-safe") {
                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[ERC-2098 short signatures]
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        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 recovered, RecoverError err, bytes32 errArg) {
        // 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);
        }
    }
}
Biz.sol 355 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {BizGuard} from "./BizGuard.sol";
import {BizHandler} from "./BizHandler.sol";

/**
 * @title Biz - A secure smart wallet for delegated EOAs using EIP-7702.
 * @dev Most important concepts:
 *      - Biz is a stateless account implementation. Only relies on BizGuard for 4337 Flag & Nonce.
 *      - Biz allows 3 distinct validation & execution flows.
 *          - ERC-4337.
 *          - Self-Call.
 *          - Signature based. msg.sender Agnostic.
 *      - ERC-4337 support is optional and it is controlled by BizGuard for extra layer of security.
 *      - Biz supports ERC-7779.
 * @author David Kim - @PowerStream3604
 */
contract Biz is BizHandler {
    /// @dev The packed ERC4337 user operation (userOp) struct.
    struct PackedUserOperation {
        address sender;
        uint256 nonce;
        bytes initCode; // Factory address and `factoryData` (or empty).
        bytes callData;
        bytes32 accountGasLimits; // `verificationGas` (16 bytes) and `callGas` (16 bytes).
        uint256 preVerificationGas;
        bytes32 gasFees; // `maxPriorityFee` (16 bytes) and `maxFeePerGas` (16 bytes).
        bytes paymasterAndData; // Paymaster fields (or empty).
        bytes signature;
    }

    /// @dev Call struct for the `executeBatch` function.
    struct Call {
        address target;
        uint256 value;
        bytes data;
    }

    error OnlyEntryPoint();
    error OnlySelf();
    error InvalidSignature();
    error ERC4337Disabled();
    error InvalidERC4337Flag();
    error ZeroAddressGuard();
    error Invalid4337ExecutionSelector();

    // keccak("Biz(bytes32 userOpHash)")
    bytes32 private constant BIZ_USEROP_HASH = 0x4f51e7a567f083a31264743067875fc6a7ae45c32c5bd71f6a998c4625b13867;
    // keccak("Biz(bytes32 msgHash)")
    bytes32 private constant BIZ_MSG_HASH = 0x31322a37c2a66b24e1088197e5b24fcc050625c13d4b84c3eaa6a8be5270321d;
    // keccak("Biz(bytes32 execHash)")
    bytes32 private constant BIZ_EXEC_HASH = 0xec429430bbd6d0e373848272230d6fe2bac6319d903762e089c5cae97af53df0;
    // keccak("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)")
    bytes32 private constant BIZ_DOMAIN_SEPARATOR_HASH =
        0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472;
    // keccak("Biz(bool Flag4337)")
    bytes32 private constant BIZ_4337_FLAG = 0xa77908bfab24e6768503125e99551a4fa5c3c75f3106fee5dea5ac0cebc017a4;
    // keccak256("Biz")
    bytes32 private constant BIZ_HASH = 0x906754a21e6afe7233c7ccce2787110e84157e1cbaa80e58656dff779193a5cd;
    // keccak256("v1.0.0")
    bytes32 private constant VERSION_HASH = 0x15124d26d1272f8d4d5266a24ca397811f414b8cd05a53b26b745f63af5ae2fc;
    uint256 private constant MAGIC_VALUE = 6789;
    bytes4 internal constant ERC1271_SUCCESS = 0x1626ba7e;
    bytes4 internal constant ERC1271_FAILURE = 0xffffffff;

    BizGuard public immutable bizGuard;
    bytes32 private immutable singletonSalt;

    constructor(BizGuard _bizGuard) {
        if (address(_bizGuard) == address(0)) revert ZeroAddressGuard();
        bizGuard = _bizGuard;
        singletonSalt = _addressToBytes32(address(this));
    }

    /// @dev Requires the caller to be EntryPoint.
    modifier onlyEntryPoint() {
        if (msg.sender != entryPoint()) revert OnlyEntryPoint();
        _;
    }

    /// @dev Requires the caller to be Self.
    modifier onlySelf() {
        if (msg.sender != address(this)) revert OnlySelf();
        _;
    }

    /// @dev Requires the BizGuard to enable ERC-4337.
    modifier checkGuard() {
        if (!bizGuard.is4337Enabled()) revert ERC4337Disabled();
        _;
    }

    /**
     * @notice Validates if the transient storage flag is set.
     * @dev The transient storage tCounter is only set when the validateUserOp() is successful.
     *      For the validateUserOp() to be successful,
     *         - BizGuard should enable ERC-4337 flow
     *         - EntryPoint should be the msg.sender
     *         - Signature validation should pass
     *      tCounter is incremented per successful execution of validateUserOp()
     *      tCounter is decremented per successful execution of 4337 execution functions
     *      each ERC-4337 validation MUST be coupled with the execution functions. execute4337Op() or execute4337Ops().
     */
    modifier check4337Flag() {
        uint256 tCounter;
        bool sFlagIsMagic;

        assembly {
            tCounter := tload(BIZ_4337_FLAG)
        }

        if (tCounter == 0) {
            uint256 sFlag;
            assembly {
                sFlag := sload(BIZ_4337_FLAG)
            }
            if (sFlag != MAGIC_VALUE) revert InvalidERC4337Flag();
            sFlagIsMagic = true;
        }

        _;

        if (tCounter != 0) {
            assembly {
                tstore(BIZ_4337_FLAG, sub(tCounter, 1))
            }
        }

        if (sFlagIsMagic) {
            assembly {
                sstore(BIZ_4337_FLAG, 0)
            }
        }
    }

    /**
     * @notice Returns EntryPoint contract address.
     * @return address of EntryPoint v0.7.0.
     */
    function entryPoint() public view virtual returns (address) {
        return 0x0000000071727De22E5E9d8BAf0edAc6f37da032;
    }

    /**
     * @dev Provides the namespace of the account.
     * @return string Biz namespace.
     */
    function accountId() external pure override returns (string memory) {
        return "TrustWallet.Biz.v1.0.0";
    }

    /**
     * @notice Validates the UserOperation.
     * @dev This function can only be called by the EntryPoint.
     *      It is a prerequisite to have BizGuard enable 4337 Flag.
     * @param userOp PackedUserOperation to perform ERC-4337 validation & execution.
     * @param userOpHash Bytes32 hash of UserOperation.
     * @param missingAccountFunds Uint256 amount of funds to be sent to EntryPoint.
     * @return validationData Uint256 validation result from the account.
     */
    function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
        external
        onlyEntryPoint
        checkGuard
        returns (uint256 validationData)
    {
        bool isValid = _validateSignature(_getEncodedMsgHash(BIZ_USEROP_HASH, userOpHash), userOp.signature);

        if (
            bytes4(userOp.callData[:4]) != Biz.execute4337Op.selector
                && bytes4(userOp.callData[:4]) != Biz.execute4337Ops.selector
        ) revert Invalid4337ExecutionSelector();

        if (isValid) {
            _enable4337Flag();
            validationData = 0;
        } else {
            validationData = 1;
        }
        assembly {
            if missingAccountFunds { pop(call(gas(), caller(), missingAccountFunds, 0, 0, 0, 0)) }
        }
    }

    /**
     * @notice Validates if the signature is signed by Biz.
     * @dev This function validates the signature if Biz indeed signed the given hash. It uses EIP-712.
     * @param hash Bytes32 hash that is signed.
     * @param signature Bytes signature to be validated.
     * @return isValid Bytes4 indicating the success/failure of validation.
     */
    function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 isValid) {
        isValid =
            _validateSignature(_getEncodedMsgHash(BIZ_MSG_HASH, hash), signature) ? ERC1271_SUCCESS : ERC1271_FAILURE;
    }

    /**
     * @notice Executes the given data.
     * @dev This function performs execution and should only be called by the owner EOA.
     * @param target Address to perform execution.
     * @param value Uint256 value of native coin.
     * @param data Bytes calldata to execute.
     */
    function execute(address target, uint256 value, bytes calldata data) external payable onlySelf {
        _call(target, value, data);
    }

    /**
     * @notice Batch executes the given data.
     * @dev This function executes the array of Calls.
     * @param calls Array of execution data
     */
    function executeBatch(Call[] calldata calls) external payable onlySelf {
        uint256 length = calls.length;
        for (uint256 i; i < length; ++i) {
            _call(calls[i].target, calls[i].value, calls[i].data);
        }
    }

    /**
     * @notice Executes the given data from ERC-4337 EntryPoint.
     * @dev This function performs execution and should only be called by the EntryPoint.
     *      ERC-4337 Flag should be enabled, indicates the success of the coupled validation.
     * @param target Address to perform execution.
     * @param value Uint256 value of native coin.
     * @param data Bytes calldata to execute.
     */
    function execute4337Op(address target, uint256 value, bytes calldata data)
        external
        payable
        onlyEntryPoint
        check4337Flag
    {
        _call(target, value, data);
    }

    /**
     * @notice Batch executes the given data from ERC-4337 EntryPoint.
     * @dev This function performs execution and should only be called by the EntryPoint.
     *      ERC-4337 Flag should be enabled, indicates the success of the coupled validation.
     * @param calls Array of execution data.
     */
    function execute4337Ops(Call[] calldata calls) external payable onlyEntryPoint check4337Flag {
        uint256 length = calls.length;
        for (uint256 i; i < length; ++i) {
            _call(calls[i].target, calls[i].value, calls[i].data);
        }
    }

    /**
     * @notice Batch executes the given data after performing signature validation.
     * @dev This function performs execution and is only executed if signature validation is successful.
     * @param calls Array of execution data.
     */
    function executeWithSignature(Call[] calldata calls, bytes calldata signature) external payable {
        if (
            !_validateSignature(
                _getEncodedMsgHash(BIZ_EXEC_HASH, keccak256(abi.encode(calls, bizGuard.incrementNonce()))), signature
            )
        ) {
            revert InvalidSignature();
        }

        uint256 length = calls.length;
        for (uint256 i; i < length; ++i) {
            _call(calls[i].target, calls[i].value, calls[i].data);
        }
    }

    /**
     * @notice Receives native coin without data.
     */
    receive() external payable {}

    /**
     * @notice Accepts incoming calls (with or without value), to mimic an EOA.
     */
    fallback() external payable {}

    /**
     * @notice Validates if the signature is signed by Biz.
     * @dev Uses ECDSA from Openzeppelin library.
     * @param hash Bytes32 hash that is signed.
     * @param signature Bytes signature to be validated.
     * @return isValid Bytes4 indicating the success/failure of validation.
     */
    function _validateSignature(bytes32 hash, bytes calldata signature) private view returns (bool isValid) {
        isValid = (address(this) == ECDSA.recover(hash, signature));
    }

    /**
     * @notice Generate encoded message hash from raw hash.
     * @dev Uses EIP-712 encoding to wrap hash with typehash & domain separator.
     * @param typeHash Bytes32 of Biz message.
     * @param hash Bytes32 original hash.
     * @return encodedMsgHash Encoded message hash.
     */
    function _getEncodedMsgHash(bytes32 typeHash, bytes32 hash) private view returns (bytes32 encodedMsgHash) {
        bytes32 messageHash = keccak256(abi.encode(typeHash, hash));
        encodedMsgHash = keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), messageHash));
    }

    /**
     * @notice Returns the domain separator
     * @dev Includes the address of singleton for signature resilience when re-delegation happens.
     * @return domainSeparator Bytes32 domain separator
     */
    function _domainSeparator() private view returns (bytes32 domainSeparator) {
        return keccak256(
            abi.encode(BIZ_DOMAIN_SEPARATOR_HASH, BIZ_HASH, VERSION_HASH, block.chainid, address(this), singletonSalt)
        );
    }

    /**
     * @notice Executes the given data.
     * @dev This function performs execution and should only be called by the owner EOA.
     * @param target Address to perform execution.
     * @param value Uint256 value of native coin.
     * @param data Bytes calldata to execute.
     */
    function _call(address target, uint256 value, bytes memory data) private {
        (bool success, bytes memory result) = target.call{value: value}(data);
        if (!success) {
            assembly {
                revert(add(result, 32), mload(result))
            }
        }
    }

    /**
     * @notice Enable 4337 Flag in transient storage.
     * @dev 4337 Flag can only be enabled when
     *      - BizGuard is enabled
     *      - Signature validation is successful
     *      - Validation call is from EntryPoint
     */
    function _enable4337Flag() private {
        assembly {
            tstore(BIZ_4337_FLAG, add(tload(BIZ_4337_FLAG), 1))
        }
    }

    /**
     * @notice Converts address to Bytes32
     * @dev Zero bytes are padded left. Address is positioned right.
     * @param _address Address to be converted to Bytes32
     */
    function _addressToBytes32(address _address) private pure returns (bytes32 result) {
        assembly {
            result := _address
        }
    }
}
BizGuard.sol 56 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";

/**
 * @title BizGuard - Guard contract for Biz.
 * @dev Most important concepts:
 *      - BizGuard maintains the nonce of all Biz for signature validation.
 *      - BizGuard is an ownable smart contract with 4337 flag to enable/disable ERC-4337 of Biz.
 * @author David Kim - @PowerStream3604
 */
contract BizGuard is Ownable2Step {
    event FlagSet(bool flag);

    mapping(address => uint256) public accountNonce;
    bool erc4337Flag;

    /**
     * @notice constructor
     * @dev Sets the initial owner of the account.
     * @param owner address.
     */
    constructor(address owner) Ownable(owner) {}

    /**
     * @notice Increment nonce of the msg.sender.
     * @dev Nonce can only increment and not decrement.
     * @return nonce uint256 nonce of the msg.sender.
     */
    function incrementNonce() external returns (uint256 nonce) {
        nonce = accountNonce[msg.sender];
        unchecked {
            ++accountNonce[msg.sender];
        }
    }

    /**
     * @notice Return if the 4337 flag is enabled.
     * @dev This view function is called by validateUserOp() in all Biz wallet.
     * @return isEnabled bool flag whether 4337 is enabled.
     */
    function is4337Enabled() external view returns (bool isEnabled) {
        isEnabled = erc4337Flag;
    }

    /**
     * @notice Set function to set 4337 Flag.
     * @dev This function can only be called by the owner.
     * @param flag bool value of the flag.
     */
    function set4337Flag(bool flag) external onlyOwner {
        erc4337Flag = flag;
        emit FlagSet(flag);
    }
}
BizHandler.sol 98 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

import {ERC721TokenReceiver} from "./interfaces/ERC721TokenReceiver.sol";
import {ERC1155TokenReceiver} from "./interfaces/ERC1155TokenReceiver.sol";
import {IERC165} from "./interfaces/IERC165.sol";
import {IERC7779} from "./interfaces/IERC7779.sol";
import {IERC1271} from "./interfaces/IERC1271.sol";

/**
 * @title BizHandler - Handler contract for Biz.
 * @author David Kim - @PowerStream3604
 */
abstract contract BizHandler is IERC7779, IERC165, ERC1155TokenReceiver, ERC721TokenReceiver {
    // keccak256(abi.encode(uint256(keccak256(bytes("InteroperableDelegatedAccount.ERC.Storage"))) - 1)) & ~bytes32(uint256(0xff));
    bytes32 internal constant ERC7779_STORAGE_BASE = 0xc473de86d0138e06e4d4918a106463a7cc005258d2e21915272bcb4594c18900;

    struct ERC7779Storage {
        bytes32[] storageBases;
    }

    /**
     * @notice Returns if the interfaceId is supported by Biz.
     * @param interfaceId Bytes4 interface id.
     * @return bool Bool representing if interface id is supported.
     */
    function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
        return interfaceId == type(ERC1155TokenReceiver).interfaceId
            || interfaceId == type(ERC721TokenReceiver).interfaceId || interfaceId == type(IERC165).interfaceId
            || interfaceId == type(IERC7779).interfaceId || interfaceId == type(IERC1271).interfaceId;
    }

    /**
     * @notice Handles ERC721 Token callback.
     * @return bytes4 Standardized onERC721Received selector.
     */
    function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) {
        return this.onERC721Received.selector;
    }

    /**
     * @notice Handles ERC1155 Token callback.
     * @return bytes4 Standardized onERC1155Received selector.
     */
    function onERC1155Received(address, address, uint256, uint256, bytes calldata)
        external
        pure
        override
        returns (bytes4)
    {
        return this.onERC1155Received.selector;
    }

    /**
     * @notice Handles ERC1155 Token batch callback.
     * @return bytes4 Standardized onERC1155BatchReceived selector.
     */
    function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata)
        external
        pure
        override
        returns (bytes4)
    {
        return this.onERC1155BatchReceived.selector;
    }

    /**
     * @notice Handles ERC777 Token callback.
     * Does not return value, empty implementation.
     */
    function tokensReceived(address, address, address, uint256, bytes calldata, bytes calldata) external pure {}

    /**
     * @notice Handles ERC677 Token callback.
     * @return bool true.
     */
    function onTokenTransfer(address, uint256, bytes calldata) external pure returns (bool) {
        return true;
    }

    /**
     * @dev Provides the namespace of the account.
     * @return string Biz namespace.
     */
    function accountId() external pure virtual returns (string memory) {}

    /**
     * @dev Provides the array of storage bases.
     * @return bytes32[] of storage bases.
     */
    function accountStorageBases() external view returns (bytes32[] memory) {
        ERC7779Storage storage $;
        assembly {
            $.slot := ERC7779_STORAGE_BASE
        }
        return $.storageBases;
    }
}
ERC1155TokenReceiver.sol 43 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

// Note: The ERC-165 identifier for this interface is 0x4e2312e0.
interface ERC1155TokenReceiver {
    /**
     * @notice Handle the receipt of a single ERC1155 token type.
     * @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.
     *      This function MUST return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61) if it accepts the transfer.
     *      This function MUST revert if it rejects the transfer.
     *      Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
     * @param _operator  The address which initiated the transfer (i.e. msg.sender).
     * @param _from      The address which previously owned the token.
     * @param _id        The ID of the token being transferred.
     * @param _value     The amount of tokens being transferred.
     * @param _data      Additional data with no specified format.
     * @return           `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
     */
    function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data)
        external
        returns (bytes4);

    /**
     * @notice Handle the receipt of multiple ERC1155 token types.
     * @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.
     *      This function MUST return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81) if it accepts the transfer(s).
     *      This function MUST revert if it rejects the transfer(s).
     *      Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
     * @param _operator  The address which initiated the batch transfer (i.e. msg.sender).
     * @param _from      The address which previously owned the token.
     * @param _ids       An array containing ids of each token being transferred (order and length must match _values array).
     * @param _values    An array containing amounts of each token being transferred (order and length must match _ids array).
     * @param _data      Additional data with no specified format.
     * @return           `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
     */
    function onERC1155BatchReceived(
        address _operator,
        address _from,
        uint256[] calldata _ids,
        uint256[] calldata _values,
        bytes calldata _data
    ) external returns (bytes4);
}
ERC721TokenReceiver.sol 23 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
interface ERC721TokenReceiver {
    /**
     * @notice Handle the receipt of an NFT
     * @dev The ERC721 smart contract calls this function on the recipient
     *  after a `transfer`. This function MAY throw to revert and reject the
     *  transfer. Return of other than the magic value MUST result in the
     *  transaction being reverted.
     *  Note: the contract address is always the message sender.
     * @param _operator The address which called `safeTransferFrom` function.
     * @param _from The address which previously owned the token.
     * @param _tokenId The NFT identifier which is being transferred.
     * @param _data Additional data with no specified format.
     * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
     *  unless throwing
     */
    function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data)
        external
        returns (bytes4);
}
IERC1271.sol 15 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided hash
     * @param _hash      Hash of the data to be signed
     * @param _signature Signature byte array associated with _hash
     *
     * MUST return the bytes4 magic value 0x1626ba7e when function passes.
     * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
     * MUST allow external calls
     */
    function isValidSignature(bytes32 _hash, bytes memory _signature) external view returns (bytes4);
}
IERC165.sol 15 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

/// @notice More details at https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/IERC165.sol
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by `interfaceId`.
     * See the corresponding EIP section
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
IERC7779.sol 25 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

interface IERC7779 {
    /*
    * @dev    Provides the namespace of the account.
    *         namespace of accounts can possibly include, account version, account name, wallet vendor name, etc
    * @notice this standard does not standardize the namespace format
    * e.g.,   "v0.1.2.7702Account.WalletProjectA"
    */
    function accountId() external view returns (string memory);

    /*
    * @dev    Externally shares the storage bases that has been used throughout the account.
    *         Majority of 7702 accounts will have their distinctive storage base to reduce the chance of storage collision.
    *         This allows the external entities to know what the storage base is of the account.
    *         Wallets willing to redelegate already-delegated accounts should call accountStorageBase() to check if it confirms with the account it plans to redelegate.
    *
    *         The bytes32 array should be stored at the storage slot: keccak(keccak('InteroperableDelegatedAccount.ERC.Storage')-1) & ~0xff
    *         This is an append-only array so newly redelegated accounts should not overwrite the storage at this slot, but just append their base to the array.
    *         This append operation should be done during the initialization of the account.
    * 		   This array should return a value of keccak hash unless using external storage.
    */
    function accountStorageBases() external view returns (bytes32[] memory);
}

Read Contract

accountId 0x9cfd7cff → string
accountStorageBases 0x8626e88b → bytes32[]
bizGuard 0xcf21ecab → address
entryPoint 0xb0d691fe → address
isValidSignature 0x1626ba7e → bytes4
onERC1155BatchReceived 0xbc197c81 → bytes4
onERC1155Received 0xf23a6e61 → bytes4
onERC721Received 0x150b7a02 → bytes4
onTokenTransfer 0xa4c0ed36 → bool
supportsInterface 0x01ffc9a7 → bool
tokensReceived 0x0023de29

Write Contract 6 functions

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

execute 0xb61d27f6
address target
uint256 value
bytes data
execute4337Op 0x76276c82
address target
uint256 value
bytes data
execute4337Ops 0x3e1a0339
tuple[] calls
executeBatch 0xe3f3e7ec
tuple[] calls
executeWithSignature 0xcc7cb962
tuple[] calls
bytes signature
validateUserOp 0x50b589d2
tuple userOp
bytes32 userOpHash
uint256 missingAccountFunds
returns: uint256

Token Balances (1) $240.00

View Transfers →
TokenBalancePriceValue
USDC 240 $1.00 $240.00

Recent Transactions

This address has 1 on-chain transactions, but only 1.6% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →