ETH Price: $2,022.14 (+1.97%)

Contract Diff Checker

Contract Name:
BasicSmartAccount

Contract Source Code:

File 1 of 1 : BasicSmartAccount

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.30;

/**
 * @title BasicSmartAccount - This contract support batch execution of transactions.
 * The only storage is a nonce to prevent replay attacks.
 * The contract is intended to be used with EIP-7702 where EOA delegates to this contract.
 */
contract BasicSmartAccount {
    struct Storage {
        uint256 nonce;
    }

    // Reserve a unique storage slot for the nonce.
    // * keccak256("BasicSmartAccount") & (~0xff)
    bytes32 private constant _STORAGE =
        0xbdfee0231e0903cde9ca6fd75d08a500062dc3d87718f712bc6958ed69761700;

    // Domain typehash for EIP712 message.
    // * keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
    bytes32 private constant _DOMAIN_TYPEHASH =
        0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;

    // The struct typehash for the EIP712 message.
    // * keccak256("HandleOps(bytes32 data,uint256 nonce)")
    bytes32 private constant _HANDLEOPS_TYPEHASH =
        0x4f8bb4631e6552ac29b9d6bacf60ff8b5481e2af7c2104fe0261045fa6988111;

    address private immutable ENTRY_POINT;

    error InvalidSignature();

    /**
     * @dev Sends multiple transactions with signature validation and reverts all if one fails.
     * @param userOps Encoded User Ops.
     * @param r The r part of the signature.
     * @param vs The v and s part of the signature.
     */
    function handleOps(
        bytes memory userOps,
        uint256 r,
        uint256 vs
    ) public payable {
        Storage storage $ = _storage();
        uint256 nonce = $.nonce;

        // Calculate the hash of transactions data and nonce for signature verification
        bytes32 domainSeparator = keccak256(
            abi.encode(_DOMAIN_TYPEHASH, block.chainid, address(this))
        );

        bytes32 structHash = keccak256(
            abi.encode(_HANDLEOPS_TYPEHASH, keccak256(userOps), nonce)
        );
        bytes32 digest = keccak256(
            abi.encodePacked("\x19\x01", domainSeparator, structHash)
        );

        // Verify the signature of EIP712 message
        require(_isValidSignature(digest, r, vs), InvalidSignature());

        // Update nonce for the sender to prevent replay attacks
        unchecked {
            $.nonce = nonce + 1;
        }

        /* solhint-disable no-inline-assembly */
        assembly ("memory-safe") {
            let length := mload(userOps)
            let i := 0x20
            for {

            } lt(i, length) {

            } {
                let to := shr(0x60, mload(add(userOps, i)))
                let value := mload(add(userOps, add(i, 0x14)))
                let dataLength := mload(add(userOps, add(i, 0x34)))
                let data := add(userOps, add(i, 0x54))
                let success := call(gas(), to, value, data, dataLength, 0, 0)

                if eq(success, 0) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
                i := add(i, add(0x54, dataLength))
            }
        }
        /* solhint-enable no-inline-assembly */
    }

    /**
     * @dev Validates the signature by extracting `v` and `s` from `vs` and using `ecrecover`.
     * @param hash The hash of the signed data.
     * @param r The r part of the signature.
     * @param vs The v and s part of the signature combined.
     * @return bool True if the signature is valid, false otherwise.
     */
    function _isValidSignature(
        bytes32 hash,
        uint256 r,
        uint256 vs
    ) internal view returns (bool) {
        unchecked {
            uint256 v = (vs >> 255) + 27;
            uint256 s = vs &
                0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

            return
                address(this) ==
                ecrecover(hash, uint8(v), bytes32(r), bytes32(s));
        }
    }

    function _storage() private pure returns (Storage storage $) {
        assembly ("memory-safe") {
            $.slot := _STORAGE
        }
    }

    function getNonce() external view returns (uint256) {
        return _storage().nonce;
    }

    // Allow the contract to receive ETH
    fallback() external payable {}

    receive() external payable {}
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):