ETH Price: $2,435.14 (+0.12%)

Transaction Decoder

Block:
22788450 at Jun-26-2025 11:54:47 AM +UTC
Transaction Fee:
0.000167376470573687 ETH $0.41
Gas Used:
87,151 Gas / 1.920534137 Gwei

Emitted Events:

335 0x114e07360014efd57c9fb09f3311d32ecc5a7254.0x6c9d76c1677b0909ce573fabdda9f1bbbc891f981a7efa98415bdcd0ff9f5411( 0x6c9d76c1677b0909ce573fabdda9f1bbbc891f981a7efa98415bdcd0ff9f5411, 0000000000000000000000004eb2dcd85845ae6591dc2736e5cf3956f3de19ca )

Account State Difference:

  Address   Before After State Difference Code
0x114e0736...eCc5A7254
0.00132096016648416 Eth
Nonce: 1
0.001153583695910473 Eth
Nonce: 4
0.000167376470573687From: 0 To: 22892027017778274884272170254408141213029715916794151611
0x4eB2DCd8...6f3DE19cA
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 57617015923227607092762252245663329320321667250174806689226035272783136532595637856697627360362327255071632630946665422476283051061192975524152861807237309043557491786067129451278922324
(beaverbuild)
13.597501632501975081 Eth13.597574187965640169 Eth0.000072555463665088

Execution Trace

0x114e07360014efd57c9fb09f3311d32ecc5a7254.CALL( )
  • Storage.61004d3d( )
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity 0.8.23;
    import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
    import {IStorage} from "./interfaces/IStorage.sol";
    import {WalletCoreLib} from "./lib/WalletCoreLib.sol";
    import {Errors} from "./lib/Errors.sol";
    contract Storage is IStorage {
        //mutable storage
        uint256 private _nonce;
        mapping(address => bool) private _validValidator;
        mapping(uint256 => bool) private _invalidSessionId;
        /**
         * @notice Restricts function access to the wallet owner only
         * @dev Reverts with INVALID_OWNER if caller is not the owner
         */
        modifier onlyOwner() {
            if (msg.sender != getOwner()) {
                revert Errors.InvalidOwner();
            }
            _;
        }
        /**
         * @notice Reads the current nonce and increments it for the next transaction
         * @dev Only callable by wallet owner. Uses unchecked math for gas optimization
         * @param validator The address of the validator contract
         * @return uint256 The current nonce before increment
         */
        function readAndUpdateNonce(
            address validator
        ) external onlyOwner returns (uint256) {
            validateValidator(validator);
            unchecked {
                uint256 currentNonce = _nonce++;
                emit NonceConsumed(currentNonce);
                return currentNonce;
            }
        }
        /**
         * @notice Sets a validator's whitelist status
         * @dev Only callable by wallet owner
         * @param validator Address of the validator
         * @param isValid True to whitelist, false to remove
         */
        function setValidatorStatus(
            address validator,
            bool isValid
        ) external onlyOwner {
            _validValidator[validator] = isValid;
            emit ValidatorStatusUpdated(validator, isValid);
        }
        /**
         * @notice Revokes the specified session ID, marking it as invalid.
         * @dev Only callable by wallet owner
         * @param id The session ID to be revoked
         */
        function revokeSession(uint256 id) external onlyOwner {
            _invalidSessionId[id] = true;
            emit SessionRevoked(id);
        }
        /**
         * @notice Returns the owner address of the wallet
         * @dev Decodes the owner address from the proxy contract's initialization data
         * @return address The owner address of the wallet
         */
        function getOwner() public view returns (address) {
            return abi.decode(Clones.fetchCloneArgs(address(this)), (address));
        }
        /**
         * @notice Returns the current nonce value
         * @dev Can be called by anyone
         * @return uint256 The current nonce value
         */
        function getNonce() external view returns (uint256) {
            return _nonce;
        }
        /**
         * @notice Checks if a validator is whitelisted
         * @dev Reverts if validator is not whitelisted
         * @param validator Address of the validator to check
         */
        function validateValidator(address validator) public view {
            if (
                validator != WalletCoreLib.SELF_VALIDATION_ADDRESS &&
                !_validValidator[validator]
            ) revert Errors.InvalidValidator(validator);
        }
        /**
         * @notice Validates a session and its associated validator
         * @dev Reverts if:
         *      - Session is invalid (blacklisted)
         *      - Validator is not activated
         * @param id The ID of the session to validate
         * @param validator The validator address (0x0 to skip validator check)
         */
        function validateSession(uint256 id, address validator) external view {
            if (_invalidSessionId[id]) revert Errors.InvalidSessionId();
            validateValidator(validator);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.2.0) (proxy/Clones.sol)
    pragma solidity ^0.8.20;
    import {Create2} from "../utils/Create2.sol";
    import {Errors} from "../utils/Errors.sol";
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
     * deploying minimal proxy contracts, also known as "clones".
     *
     * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
     * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
     *
     * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
     * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
     * deterministic method.
     */
    library Clones {
        error CloneArgumentsTooLong();
        /**
         * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
         *
         * This function uses the create opcode, which should never revert.
         */
        function clone(address implementation) internal returns (address instance) {
            return clone(implementation, 0);
        }
        /**
         * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
         * to the new contract.
         *
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
         */
        function clone(address implementation, uint256 value) internal returns (address instance) {
            if (address(this).balance < value) {
                revert Errors.InsufficientBalance(address(this).balance, value);
            }
            assembly ("memory-safe") {
                // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                // of the `implementation` address with the bytecode before the address.
                mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                instance := create(value, 0x09, 0x37)
            }
            if (instance == address(0)) {
                revert Errors.FailedDeployment();
            }
        }
        /**
         * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
         *
         * This function uses the create2 opcode and a `salt` to deterministically deploy
         * the clone. Using the same `implementation` and `salt` multiple times will revert, since
         * the clones cannot be deployed twice at the same address.
         */
        function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
            return cloneDeterministic(implementation, salt, 0);
        }
        /**
         * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
         * a `value` parameter to send native currency to the new contract.
         *
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
         */
        function cloneDeterministic(
            address implementation,
            bytes32 salt,
            uint256 value
        ) internal returns (address instance) {
            if (address(this).balance < value) {
                revert Errors.InsufficientBalance(address(this).balance, value);
            }
            assembly ("memory-safe") {
                // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                // of the `implementation` address with the bytecode before the address.
                mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                instance := create2(value, 0x09, 0x37, salt)
            }
            if (instance == address(0)) {
                revert Errors.FailedDeployment();
            }
        }
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
         */
        function predictDeterministicAddress(
            address implementation,
            bytes32 salt,
            address deployer
        ) internal pure returns (address predicted) {
            assembly ("memory-safe") {
                let ptr := mload(0x40)
                mstore(add(ptr, 0x38), deployer)
                mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
                mstore(add(ptr, 0x14), implementation)
                mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
                mstore(add(ptr, 0x58), salt)
                mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
                predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
            }
        }
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
         */
        function predictDeterministicAddress(
            address implementation,
            bytes32 salt
        ) internal view returns (address predicted) {
            return predictDeterministicAddress(implementation, salt, address(this));
        }
        /**
         * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
         * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
         * access the arguments within the implementation, use {fetchCloneArgs}.
         *
         * This function uses the create opcode, which should never revert.
         */
        function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) {
            return cloneWithImmutableArgs(implementation, args, 0);
        }
        /**
         * @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
         * parameter to send native currency to the new contract.
         *
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
         */
        function cloneWithImmutableArgs(
            address implementation,
            bytes memory args,
            uint256 value
        ) internal returns (address instance) {
            if (address(this).balance < value) {
                revert Errors.InsufficientBalance(address(this).balance, value);
            }
            bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
            assembly ("memory-safe") {
                instance := create(value, add(bytecode, 0x20), mload(bytecode))
            }
            if (instance == address(0)) {
                revert Errors.FailedDeployment();
            }
        }
        /**
         * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation` with custom
         * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
         * access the arguments within the implementation, use {fetchCloneArgs}.
         *
         * This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
         * `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice
         * at the same address.
         */
        function cloneDeterministicWithImmutableArgs(
            address implementation,
            bytes memory args,
            bytes32 salt
        ) internal returns (address instance) {
            return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0);
        }
        /**
         * @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
         * but with a `value` parameter to send native currency to the new contract.
         *
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
         */
        function cloneDeterministicWithImmutableArgs(
            address implementation,
            bytes memory args,
            bytes32 salt,
            uint256 value
        ) internal returns (address instance) {
            bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
            return Create2.deploy(value, salt, bytecode);
        }
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
         */
        function predictDeterministicAddressWithImmutableArgs(
            address implementation,
            bytes memory args,
            bytes32 salt,
            address deployer
        ) internal pure returns (address predicted) {
            bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
            return Create2.computeAddress(salt, keccak256(bytecode), deployer);
        }
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
         */
        function predictDeterministicAddressWithImmutableArgs(
            address implementation,
            bytes memory args,
            bytes32 salt
        ) internal view returns (address predicted) {
            return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this));
        }
        /**
         * @dev Get the immutable args attached to a clone.
         *
         * - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
         *   function will return an empty array.
         * - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
         *   `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
         *   creation.
         * - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
         *   function should only be used to check addresses that are known to be clones.
         */
        function fetchCloneArgs(address instance) internal view returns (bytes memory) {
            bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
            assembly ("memory-safe") {
                extcodecopy(instance, add(result, 32), 45, mload(result))
            }
            return result;
        }
        /**
         * @dev Helper that prepares the initcode of the proxy with immutable args.
         *
         * An assembly variant of this function requires copying the `args` array, which can be efficiently done using
         * `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
         * abi.encodePacked is more expensive but also more portable and easier to review.
         *
         * NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
         * With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
         */
        function _cloneCodeWithImmutableArgs(
            address implementation,
            bytes memory args
        ) private pure returns (bytes memory) {
            if (args.length > 24531) revert CloneArgumentsTooLong();
            return
                abi.encodePacked(
                    hex"61",
                    uint16(args.length + 45),
                    hex"3d81600a3d39f3363d3d373d3d3d363d73",
                    implementation,
                    hex"5af43d82803e903d91602b57fd5bf3",
                    args
                );
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.8.23;
    interface IStorage {
        // EVENTS
        event NonceConsumed(uint256 utilisedNonce);
        event ValidatorStatusUpdated(address validator, bool status);
        event SessionRevoked(uint256 id);
        // FUNCTIONS
        function readAndUpdateNonce(address validator) external returns (uint256);
        function setValidatorStatus(address validator, bool isValid) external;
        function revokeSession(uint256 id) external;
        function getOwner() external view returns (address);
        function getNonce() external view returns (uint256);
        function validateValidator(address validator) external view;
        function validateSession(uint256 id, address validator) external view;
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.8.23;
    import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
    import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    import {IValidator} from "../interfaces/IValidator.sol";
    library WalletCoreLib {
        using ECDSA for bytes32;
        using Clones for address;
        /**
         * @notice new storage should have a different salt
         */
        bytes32 public constant STORAGE_SALT =
            keccak256(abi.encodePacked("storage"));
        bytes32 public constant VALIDATOR_SALT =
            keccak256(abi.encodePacked("validator"));
        address public constant SELF_VALIDATION_ADDRESS = address(1);
        /**
         * @notice Computes the deterministic address of the wallet's storage contract
         * @dev Uses OpenZeppelin's Clones library to predict the address before deployment
         * @param storageImpl The implementation address of the storage contract
         * @return address The deterministic address where the storage clone will be deployed
         * @custom:args The immutable arguments encoded are:
         *  - address(this): The wallet address that owns this storage
         * @custom:salt A unique salt derived from STORAGE_SALT
         */
        function _getStorage(address storageImpl) internal view returns (address) {
            return
                storageImpl.predictDeterministicAddressWithImmutableArgs(
                    abi.encode(address(this)),
                    STORAGE_SALT,
                    address(this)
                );
        }
        /**
         * @notice Validates a transaction or operation using either ECDSA signatures or an external validator contract
         * @dev Two validation methods are supported:
         *      1. ECDSA validation (when validator == address(1)): Recovers signer from signature and verifies it matches the wallet address
         *      2. External validator (any other address): Calls the validator contract and checks if it's authorized to validate
         * @param validator Address of the validator to use (address(1) for ECDSA signature validation)
         * @param typedDataHash EIP-712 typed data hash of the data to be validated
         * @param validationData For ECDSA: the 65-byte signature; For external validators: custom validation data
         * @return bool True if validation succeeds, false otherwise
         * @custom:security Ensure validator contracts are properly verified and authorized before use
         */
        function validate(
            address validator,
            bytes32 typedDataHash,
            bytes calldata validationData
        ) internal view returns (bool) {
            if (validator == SELF_VALIDATION_ADDRESS) {
                return _validateSelf(typedDataHash, validationData);
            } else {
                try IValidator(validator).validate(typedDataHash, validationData) {
                    return true;
                } catch {
                    return false;
                }
            }
        }
        /**
         * @notice Validates that a signature was signed by this contract
         * @param typedDataHash The hash of the data that was signed
         * @param signature The ECDSA signature to verify
         * @return bool True if the validation passes, false otherwise
         * @dev Reverts with INVALID_SIGNATURE if the signer is not account itself
         */
        function _validateSelf(
            bytes32 typedDataHash,
            bytes calldata signature
        ) internal view returns (bool) {
            (address recoveredSigner, , ) = typedDataHash.tryRecover(signature);
            return recoveredSigner == address(this);
        }
        /**
         * @notice Creates a unique deployment salt by combining validator implementation and init code
         * @param validatorImpl The validator implementation address
         * @param initHash Hash of the validator's initialization code
         * @return bytes32 The computed salt for deterministic deployment
         */
        function _computeCreationSalt(
            address validatorImpl,
            bytes32 initHash
        ) internal pure returns (bytes32) {
            return keccak256(abi.encode(validatorImpl, initHash));
        }
        /**
         * @notice Computes the deterministic address of a validator contract before deployment
         * @param validatorImpl The implementation address of the validator
         * @param immutableArgs The initialization data for the validator
         * @param creationSalt A unique salt for deterministic deployment
         * @param deployer The address that will deploy the validator
         * @return The predicted address where the validator will be deployed
         */
        function _computeValidatorAddress(
            address validatorImpl,
            bytes calldata immutableArgs,
            bytes32 creationSalt,
            address deployer
        ) internal pure returns (address) {
            return
                validatorImpl.predictDeterministicAddressWithImmutableArgs(
                    immutableArgs,
                    creationSalt,
                    deployer
                );
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.8.23;
    library Errors {
        // Storage related
        error InvalidExecutor();
        error InvalidSession();
        error InvalidSessionId();
        error InvalidOwner();
        // Account related
        error NotFromSelf();
        // Call related
        error CallFailed(uint256 index, bytes returnData);
        // ValidationLogic related
        error InvalidValidator(address validator);
        error InvalidValidatorImpl(address validatorImpl);
        // ECDSAValidator related
        error InvalidSignature();
        // WalletCoreBase related
        error NameTooLong();
        error VersionTooLong();
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol)
    pragma solidity ^0.8.20;
    import {Errors} from "./Errors.sol";
    /**
     * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
     * `CREATE2` can be used to compute in advance the address where a smart
     * contract will be deployed, which allows for interesting new mechanisms known
     * as 'counterfactual interactions'.
     *
     * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
     * information.
     */
    library Create2 {
        /**
         * @dev There's no code to deploy.
         */
        error Create2EmptyBytecode();
        /**
         * @dev Deploys a contract using `CREATE2`. The address where the contract
         * will be deployed can be known in advance via {computeAddress}.
         *
         * The bytecode for a contract can be obtained from Solidity with
         * `type(contractName).creationCode`.
         *
         * Requirements:
         *
         * - `bytecode` must not be empty.
         * - `salt` must have not been used for `bytecode` already.
         * - the factory must have a balance of at least `amount`.
         * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
         */
        function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
            if (address(this).balance < amount) {
                revert Errors.InsufficientBalance(address(this).balance, amount);
            }
            if (bytecode.length == 0) {
                revert Create2EmptyBytecode();
            }
            assembly ("memory-safe") {
                addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
                // if no address was created, and returndata is not empty, bubble revert
                if and(iszero(addr), not(iszero(returndatasize()))) {
                    let p := mload(0x40)
                    returndatacopy(p, 0, returndatasize())
                    revert(p, returndatasize())
                }
            }
            if (addr == address(0)) {
                revert Errors.FailedDeployment();
            }
        }
        /**
         * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
         * `bytecodeHash` or `salt` will result in a new destination address.
         */
        function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
            return computeAddress(salt, bytecodeHash, address(this));
        }
        /**
         * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
         * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
         */
        function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
            assembly ("memory-safe") {
                let ptr := mload(0x40) // Get free memory pointer
                // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
                // |-------------------|---------------------------------------------------------------------------|
                // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
                // | salt              |                                      BBBBBBBBBBBBB...BB                   |
                // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
                // | 0xFF              |            FF                                                             |
                // |-------------------|---------------------------------------------------------------------------|
                // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
                // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
                mstore(add(ptr, 0x40), bytecodeHash)
                mstore(add(ptr, 0x20), salt)
                mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
                let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
                mstore8(start, 0xff)
                addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Collection of common custom errors used in multiple contracts
     *
     * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
     * It is recommended to avoid relying on the error API for critical functionality.
     *
     * _Available since v5.1._
     */
    library Errors {
        /**
         * @dev The ETH balance of the account is not enough to perform the operation.
         */
        error InsufficientBalance(uint256 balance, uint256 needed);
        /**
         * @dev A call to an address target failed. The target may have reverted.
         */
        error FailedCall();
        /**
         * @dev The deployment failed.
         */
        error FailedDeployment();
        /**
         * @dev A necessary precompile is missing.
         */
        error MissingPrecompile(address);
    }
    // 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);
            }
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.8.23;
    interface IValidator {
        function validate(
            bytes32 msgHash,
            bytes calldata validationData
        ) external view;
    }