ETH Price: $3,070.91 (-3.53%)

Contract

0xd9AB5096a832b9ce79914329DAEE236f8Eea0390
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Enable194113022024-03-11 10:36:59248 days ago1710153419IN
0xd9AB5096...f8Eea0390
0 ETH0.0030602866.3290044

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
211884082024-11-14 20:46:111 hr ago1731617171
0xd9AB5096...f8Eea0390
0 ETH
211884082024-11-14 20:46:111 hr ago1731617171
0xd9AB5096...f8Eea0390
0 ETH
211872072024-11-14 16:44:235 hrs ago1731602663
0xd9AB5096...f8Eea0390
0 ETH
211872072024-11-14 16:44:235 hrs ago1731602663
0xd9AB5096...f8Eea0390
0 ETH
211872072024-11-14 16:44:235 hrs ago1731602663
0xd9AB5096...f8Eea0390
0 ETH
211871982024-11-14 16:42:355 hrs ago1731602555
0xd9AB5096...f8Eea0390
0 ETH
211871982024-11-14 16:42:355 hrs ago1731602555
0xd9AB5096...f8Eea0390
0 ETH
211871982024-11-14 16:42:355 hrs ago1731602555
0xd9AB5096...f8Eea0390
0 ETH
211871292024-11-14 16:28:476 hrs ago1731601727
0xd9AB5096...f8Eea0390
0 ETH
211871292024-11-14 16:28:476 hrs ago1731601727
0xd9AB5096...f8Eea0390
0 ETH
211871292024-11-14 16:28:476 hrs ago1731601727
0xd9AB5096...f8Eea0390
0 ETH
211871262024-11-14 16:28:116 hrs ago1731601691
0xd9AB5096...f8Eea0390
0 ETH
211871262024-11-14 16:28:116 hrs ago1731601691
0xd9AB5096...f8Eea0390
0 ETH
211871262024-11-14 16:28:116 hrs ago1731601691
0xd9AB5096...f8Eea0390
0 ETH
211871262024-11-14 16:28:116 hrs ago1731601691
0xd9AB5096...f8Eea0390
0 ETH
211866092024-11-14 14:44:237 hrs ago1731595463
0xd9AB5096...f8Eea0390
0 ETH
211866092024-11-14 14:44:237 hrs ago1731595463
0xd9AB5096...f8Eea0390
0 ETH
211866092024-11-14 14:44:237 hrs ago1731595463
0xd9AB5096...f8Eea0390
0 ETH
211865992024-11-14 14:42:237 hrs ago1731595343
0xd9AB5096...f8Eea0390
0 ETH
211865992024-11-14 14:42:237 hrs ago1731595343
0xd9AB5096...f8Eea0390
0 ETH
211865992024-11-14 14:42:237 hrs ago1731595343
0xd9AB5096...f8Eea0390
0 ETH
211865942024-11-14 14:41:237 hrs ago1731595283
0xd9AB5096...f8Eea0390
0 ETH
211865942024-11-14 14:41:237 hrs ago1731595283
0xd9AB5096...f8Eea0390
0 ETH
211865942024-11-14 14:41:237 hrs ago1731595283
0xd9AB5096...f8Eea0390
0 ETH
211865912024-11-14 14:40:477 hrs ago1731595247
0xd9AB5096...f8Eea0390
0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ECDSAValidator

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 8 : ECDSAValidator.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "solady/utils/ECDSA.sol";
import "src/utils/KernelHelper.sol";
import "src/interfaces/IValidator.sol";
import "src/common/Types.sol";

struct ECDSAValidatorStorage {
    address owner;
}

contract ECDSAValidator is IKernelValidator {
    event OwnerChanged(address indexed kernel, address indexed oldOwner, address indexed newOwner);

    mapping(address => ECDSAValidatorStorage) public ecdsaValidatorStorage;

    function disable(bytes calldata) external payable override {
        delete ecdsaValidatorStorage[msg.sender];
    }

    function enable(bytes calldata _data) external payable override {
        address owner = address(bytes20(_data[0:20]));
        address oldOwner = ecdsaValidatorStorage[msg.sender].owner;
        ecdsaValidatorStorage[msg.sender].owner = owner;
        emit OwnerChanged(msg.sender, oldOwner, owner);
    }

    function validateUserOp(UserOperation calldata _userOp, bytes32 _userOpHash, uint256)
        external
        payable
        override
        returns (ValidationData validationData)
    {
        address owner = ecdsaValidatorStorage[_userOp.sender].owner;
        bytes32 hash = ECDSA.toEthSignedMessageHash(_userOpHash);
        if (owner == ECDSA.recover(hash, _userOp.signature)) {
            return ValidationData.wrap(0);
        }
        if (owner != ECDSA.recover(_userOpHash, _userOp.signature)) {
            return SIG_VALIDATION_FAILED;
        }
    }

    function validateSignature(bytes32 hash, bytes calldata signature) public view override returns (ValidationData) {
        address owner = ecdsaValidatorStorage[msg.sender].owner;
        if (owner == ECDSA.recover(hash, signature)) {
            return ValidationData.wrap(0);
        }
        bytes32 ethHash = ECDSA.toEthSignedMessageHash(hash);
        address recovered = ECDSA.recover(ethHash, signature);
        if (owner != recovered) {
            return SIG_VALIDATION_FAILED;
        }
        return ValidationData.wrap(0);
    }

    function validCaller(address _caller, bytes calldata) external view override returns (bool) {
        return ecdsaValidatorStorage[msg.sender].owner == _caller;
    }
}

File 2 of 8 : ECDSA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
library ECDSA {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The signature is invalid.
    error InvalidSignature();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The number which `s` must not exceed in order for
    /// the signature to be non-malleable.
    bytes32 private constant _MALLEABILITY_THRESHOLD =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    RECOVERY OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: as of Solady version 0.0.68, these functions will
    // revert upon recovery failure for more safety by default.

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the `signature`.
    ///
    /// This function does NOT accept EIP-2098 short form signatures.
    /// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
    /// short form signatures instead.
    function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Copy the free memory pointer so that we can restore it later.
            let m := mload(0x40)
            // Copy `r` and `s`.
            mstore(0x40, mload(add(signature, 0x20))) // `r`.
            let s := mload(add(signature, 0x40))
            mstore(0x60, s)
            // Store the `hash` in the scratch space.
            mstore(0x00, hash)
            // Compute `v` and store it in the scratch space.
            mstore(0x20, byte(0, mload(add(signature, 0x60))))
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    and(
                        // If the signature is exactly 65 bytes in length.
                        eq(mload(signature), 65),
                        // If `s` in lower half order, such that the signature is not malleable.
                        lt(s, add(_MALLEABILITY_THRESHOLD, 1))
                    ), // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x00, // Start of output.
                    0x20 // Size of output.
                )
            )
            result := mload(0x00)
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                // Store the function selector of `InvalidSignature()`.
                mstore(0x00, 0x8baa579f)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the zero slot.
            mstore(0x60, 0)
            // Restore the free memory pointer.
            mstore(0x40, m)
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the `signature`.
    ///
    /// This function does NOT accept EIP-2098 short form signatures.
    /// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
    /// short form signatures instead.
    function recoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Copy the free memory pointer so that we can restore it later.
            let m := mload(0x40)
            // Directly copy `r` and `s` from the calldata.
            calldatacopy(0x40, signature.offset, 0x40)
            // Store the `hash` in the scratch space.
            mstore(0x00, hash)
            // Compute `v` and store it in the scratch space.
            mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40))))
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    and(
                        // If the signature is exactly 65 bytes in length.
                        eq(signature.length, 65),
                        // If `s` in lower half order, such that the signature is not malleable.
                        lt(mload(0x60), add(_MALLEABILITY_THRESHOLD, 1))
                    ), // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x00, // Start of output.
                    0x20 // Size of output.
                )
            )
            result := mload(0x00)
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                // Store the function selector of `InvalidSignature()`.
                mstore(0x00, 0x8baa579f)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the zero slot.
            mstore(0x60, 0)
            // Restore the free memory pointer.
            mstore(0x40, m)
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    ///
    /// This function only accepts EIP-2098 short form signatures.
    /// See: https://eips.ethereum.org/EIPS/eip-2098
    ///
    /// To be honest, I do not recommend using EIP-2098 signatures
    /// for simplicity, performance, and security reasons. Most if not
    /// all clients support traditional non EIP-2098 signatures by default.
    /// As such, this method is intentionally not fully inlined.
    /// It is merely included for completeness.
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
        uint8 v;
        bytes32 s;
        /// @solidity memory-safe-assembly
        assembly {
            s := shr(1, shl(1, vs))
            v := add(shr(255, vs), 27)
        }
        result = recover(hash, v, r, s);
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Copy the free memory pointer so that we can restore it later.
            let m := mload(0x40)
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    // If `s` in lower half order, such that the signature is not malleable.
                    lt(s, add(_MALLEABILITY_THRESHOLD, 1)), // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x00, // Start of output.
                    0x20 // Size of output.
                )
            )
            result := mload(0x00)
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                // Store the function selector of `InvalidSignature()`.
                mstore(0x00, 0x8baa579f)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the zero slot.
            mstore(0x60, 0)
            // Restore the free memory pointer.
            mstore(0x40, m)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   TRY-RECOVER OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // WARNING!
    // These functions will NOT revert upon recovery failure.
    // Instead, they will return the zero address upon recovery failure.
    // It is critical that the returned address is NEVER compared against
    // a zero address (e.g. an uninitialized address variable).

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the `signature`.
    ///
    /// This function does NOT accept EIP-2098 short form signatures.
    /// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
    /// short form signatures instead.
    function tryRecover(bytes32 hash, bytes memory signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(xor(mload(signature), 65)) {
                // Copy the free memory pointer so that we can restore it later.
                let m := mload(0x40)
                // Copy `r` and `s`.
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                let s := mload(add(signature, 0x40))
                mstore(0x60, s)
                // If `s` in lower half order, such that the signature is not malleable.
                if iszero(gt(s, _MALLEABILITY_THRESHOLD)) {
                    // Store the `hash` in the scratch space.
                    mstore(0x00, hash)
                    // Compute `v` and store it in the scratch space.
                    mstore(0x20, byte(0, mload(add(signature, 0x60))))
                    pop(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            0x01, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x40, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                    // Restore the zero slot.
                    mstore(0x60, 0)
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    result := mload(xor(0x60, returndatasize()))
                }
                // Restore the free memory pointer.
                mstore(0x40, m)
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the `signature`.
    ///
    /// This function does NOT accept EIP-2098 short form signatures.
    /// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
    /// short form signatures instead.
    function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(xor(signature.length, 65)) {
                // Copy the free memory pointer so that we can restore it later.
                let m := mload(0x40)
                // Directly copy `r` and `s` from the calldata.
                calldatacopy(0x40, signature.offset, 0x40)
                // If `s` in lower half order, such that the signature is not malleable.
                if iszero(gt(mload(0x60), _MALLEABILITY_THRESHOLD)) {
                    // Store the `hash` in the scratch space.
                    mstore(0x00, hash)
                    // Compute `v` and store it in the scratch space.
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40))))
                    pop(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            0x01, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x40, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                    // Restore the zero slot.
                    mstore(0x60, 0)
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    result := mload(xor(0x60, returndatasize()))
                }
                // Restore the free memory pointer.
                mstore(0x40, m)
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    ///
    /// This function only accepts EIP-2098 short form signatures.
    /// See: https://eips.ethereum.org/EIPS/eip-2098
    ///
    /// To be honest, I do not recommend using EIP-2098 signatures
    /// for simplicity, performance, and security reasons. Most if not
    /// all clients support traditional non EIP-2098 signatures by default.
    /// As such, this method is intentionally not fully inlined.
    /// It is merely included for completeness.
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (address result)
    {
        uint8 v;
        bytes32 s;
        /// @solidity memory-safe-assembly
        assembly {
            s := shr(1, shl(1, vs))
            v := add(shr(255, vs), 27)
        }
        result = tryRecover(hash, v, r, s);
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Copy the free memory pointer so that we can restore it later.
            let m := mload(0x40)
            // If `s` in lower half order, such that the signature is not malleable.
            if iszero(gt(s, _MALLEABILITY_THRESHOLD)) {
                // Store the `hash`, `v`, `r`, `s` in the scratch space.
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff))
                mstore(0x40, r)
                mstore(0x60, s)
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        0x01, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                // Restore the zero slot.
                mstore(0x60, 0)
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
            }
            // Restore the free memory pointer.
            mstore(0x40, m)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Store into scratch space for keccak256.
            mstore(0x20, hash)
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32")
            // 0x40 - 0x04 = 0x3c
            result := keccak256(0x04, 0x3c)
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        assembly {
            // The length of "\x19Ethereum Signed Message:\n" is 26 bytes (i.e. 0x1a).
            // If we reserve 2 words, we'll have 64 - 26 = 38 bytes to store the
            // ASCII decimal representation of the length of `s` up to about 2 ** 126.

            // Instead of allocating, we temporarily copy the 64 bytes before the
            // start of `s` data to some variables.
            let m := mload(sub(s, 0x20))
            // The length of `s` is in bytes.
            let sLength := mload(s)
            let ptr := add(s, 0x20)
            let w := not(0)
            // `end` marks the end of the memory which we will compute the keccak256 of.
            let end := add(ptr, sLength)
            // Convert the length of the bytes to ASCII decimal representation
            // and store it into the memory.
            for { let temp := sLength } 1 {} {
                ptr := add(ptr, w) // `sub(ptr, 1)`.
                mstore8(ptr, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }
            // Copy the header over to the memory.
            mstore(sub(ptr, 0x20), "\x00\x00\x00\x00\x00\x00\x19Ethereum Signed Message:\n")
            // Compute the keccak256 of the memory.
            result := keccak256(sub(ptr, 0x1a), sub(end, sub(ptr, 0x1a)))
            // Restore the previous memory.
            mstore(s, sLength)
            mstore(sub(s, 0x20), m)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes.
    function emptySignature() internal pure returns (bytes calldata signature) {
        /// @solidity memory-safe-assembly
        assembly {
            signature.length := 0
        }
    }
}

File 3 of 8 : KernelHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {SIG_VALIDATION_FAILED_UINT} from "src/common/Constants.sol";
import {ValidationData} from "src/common/Types.sol";

function _intersectValidationData(ValidationData a, ValidationData b) pure returns (ValidationData validationData) {
    assembly {
        // xor(a,b) == shows only matching bits
        // and(xor(a,b), 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff) == filters out the validAfter and validUntil bits
        // if the result is not zero, then aggregator part is not matching
        switch iszero(and(xor(a, b), 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff))
        case 1 {
            // validAfter
            let a_vd := and(0xffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff, a)
            let b_vd := and(0xffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff, b)
            validationData := xor(a_vd, mul(xor(a_vd, b_vd), gt(b_vd, a_vd)))
            // validUntil
            a_vd := and(0x000000000000ffffffffffff0000000000000000000000000000000000000000, a)
            b_vd := and(0x000000000000ffffffffffff0000000000000000000000000000000000000000, b)
            let until := xor(a_vd, mul(xor(a_vd, b_vd), lt(b_vd, a_vd)))
            if iszero(until) { until := 0x000000000000ffffffffffff0000000000000000000000000000000000000000 }
            validationData := or(validationData, until)
        }
        default { validationData := SIG_VALIDATION_FAILED_UINT }
    }
}

File 4 of 8 : IValidator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {UserOperation} from "account-abstraction/interfaces/UserOperation.sol";
import "src/common/Types.sol";

interface IKernelValidator {
    function enable(bytes calldata _data) external payable;

    function disable(bytes calldata _data) external payable;

    function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingFunds)
        external
        payable
        returns (ValidationData);

    function validateSignature(bytes32 hash, bytes calldata signature) external view returns (ValidationData);

    function validCaller(address caller, bytes calldata data) external view returns (bool);
}

// 3 modes
// 1. default mode, use preset validator for the kernel
// 2. enable mode, enable a new validator for given action and use it for current userOp
// 3. sudo mode, use default plugin for current userOp

File 5 of 8 : Types.sol
pragma solidity ^0.8.9;

import "src/common/Constants.sol";

type ValidAfter is uint48;

type ValidUntil is uint48;

type ValidationData is uint256;

ValidationData constant SIG_VALIDATION_FAILED = ValidationData.wrap(SIG_VALIDATION_FAILED_UINT);

function packValidationData(ValidAfter validAfter, ValidUntil validUntil) pure returns (ValidationData) {
    return ValidationData.wrap(
        uint256(ValidAfter.unwrap(validAfter)) << 208 | uint256(ValidUntil.unwrap(validUntil)) << 160
    );
}

function parseValidationData(ValidationData validationData)
    pure
    returns (ValidAfter validAfter, ValidUntil validUntil, address result)
{
    assembly {
        result := validationData
        validUntil := and(shr(160, validationData), 0xffffffffffff)
        switch iszero(validUntil)
        case 1 { validUntil := 0xffffffffffff }
        validAfter := shr(208, validationData)
    }
}

File 6 of 8 : Constants.sol
pragma solidity ^0.8.0;

// constants for kernel metadata
string constant KERNEL_NAME = "Kernel";
string constant KERNEL_VERSION = "0.2.1";

// ERC4337 constants
uint256 constant SIG_VALIDATION_FAILED_UINT = 1;

// STRUCT_HASH
bytes32 constant VALIDATOR_APPROVED_STRUCT_HASH = 0x3ce406685c1b3551d706d85a68afdaa49ac4e07b451ad9b8ff8b58c3ee964176;

// Storage slots
bytes32 constant KERNEL_STORAGE_SLOT = 0x439ffe7df606b78489639bc0b827913bd09e1246fa6802968a5b3694c53e0dd8;
bytes32 constant KERNEL_STORAGE_SLOT_1 = 0x439ffe7df606b78489639bc0b827913bd09e1246fa6802968a5b3694c53e0dd9;
bytes32 constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

File 7 of 8 : UserOperation.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;

/* solhint-disable no-inline-assembly */

import {calldataKeccak} from "../core/Helpers.sol";

/**
 * User Operation struct
 * @param sender the sender account of this request.
     * @param nonce unique value the sender uses to verify it is not a replay.
     * @param initCode if set, the account contract will be created by this constructor/
     * @param callData the method call to execute on this account.
     * @param callGasLimit the gas limit passed to the callData method call.
     * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp.
     * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead.
     * @param maxFeePerGas same as EIP-1559 gas parameter.
     * @param maxPriorityFeePerGas same as EIP-1559 gas parameter.
     * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific data. the paymaster will pay for the transaction instead of the sender.
     * @param signature sender-verified signature over the entire request, the EntryPoint address and the chain ID.
     */
    struct UserOperation {

        address sender;
        uint256 nonce;
        bytes initCode;
        bytes callData;
        uint256 callGasLimit;
        uint256 verificationGasLimit;
        uint256 preVerificationGas;
        uint256 maxFeePerGas;
        uint256 maxPriorityFeePerGas;
        bytes paymasterAndData;
        bytes signature;
    }

/**
 * Utility functions helpful when working with UserOperation structs.
 */
library UserOperationLib {

    function getSender(UserOperation calldata userOp) internal pure returns (address) {
        address data;
        //read sender from userOp, which is first userOp member (saves 800 gas...)
        assembly {data := calldataload(userOp)}
        return address(uint160(data));
    }

    //relayer/block builder might submit the TX with higher priorityFee, but the user should not
    // pay above what he signed for.
    function gasPrice(UserOperation calldata userOp) internal view returns (uint256) {
    unchecked {
        uint256 maxFeePerGas = userOp.maxFeePerGas;
        uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
        if (maxFeePerGas == maxPriorityFeePerGas) {
            //legacy mode (for networks that don't support basefee opcode)
            return maxFeePerGas;
        }
        return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
    }
    }

    function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) {
        address sender = getSender(userOp);
        uint256 nonce = userOp.nonce;
        bytes32 hashInitCode = calldataKeccak(userOp.initCode);
        bytes32 hashCallData = calldataKeccak(userOp.callData);
        uint256 callGasLimit = userOp.callGasLimit;
        uint256 verificationGasLimit = userOp.verificationGasLimit;
        uint256 preVerificationGas = userOp.preVerificationGas;
        uint256 maxFeePerGas = userOp.maxFeePerGas;
        uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
        bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);

        return abi.encode(
            sender, nonce,
            hashInitCode, hashCallData,
            callGasLimit, verificationGasLimit, preVerificationGas,
            maxFeePerGas, maxPriorityFeePerGas,
            hashPaymasterAndData
        );
    }

    function hash(UserOperation calldata userOp) internal pure returns (bytes32) {
        return keccak256(pack(userOp));
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
}

File 8 of 8 : Helpers.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;

/* solhint-disable no-inline-assembly */

/**
 * returned data from validateUserOp.
 * validateUserOp returns a uint256, with is created by `_packedValidationData` and parsed by `_parseValidationData`
 * @param aggregator - address(0) - the account validated the signature by itself.
 *              address(1) - the account failed to validate the signature.
 *              otherwise - this is an address of a signature aggregator that must be used to validate the signature.
 * @param validAfter - this UserOp is valid only after this timestamp.
 * @param validaUntil - this UserOp is valid only up to this timestamp.
 */
    struct ValidationData {
        address aggregator;
        uint48 validAfter;
        uint48 validUntil;
    }

//extract sigFailed, validAfter, validUntil.
// also convert zero validUntil to type(uint48).max
    function _parseValidationData(uint validationData) pure returns (ValidationData memory data) {
        address aggregator = address(uint160(validationData));
        uint48 validUntil = uint48(validationData >> 160);
        if (validUntil == 0) {
            validUntil = type(uint48).max;
        }
        uint48 validAfter = uint48(validationData >> (48 + 160));
        return ValidationData(aggregator, validAfter, validUntil);
    }

// intersect account and paymaster ranges.
    function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) pure returns (ValidationData memory) {
        ValidationData memory accountValidationData = _parseValidationData(validationData);
        ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData);
        address aggregator = accountValidationData.aggregator;
        if (aggregator == address(0)) {
            aggregator = pmValidationData.aggregator;
        }
        uint48 validAfter = accountValidationData.validAfter;
        uint48 validUntil = accountValidationData.validUntil;
        uint48 pmValidAfter = pmValidationData.validAfter;
        uint48 pmValidUntil = pmValidationData.validUntil;

        if (validAfter < pmValidAfter) validAfter = pmValidAfter;
        if (validUntil > pmValidUntil) validUntil = pmValidUntil;
        return ValidationData(aggregator, validAfter, validUntil);
    }

/**
 * helper to pack the return value for validateUserOp
 * @param data - the ValidationData to pack
 */
    function _packValidationData(ValidationData memory data) pure returns (uint256) {
        return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48));
    }

/**
 * helper to pack the return value for validateUserOp, when not using an aggregator
 * @param sigFailed - true for signature failure, false for success
 * @param validUntil last timestamp this UserOperation is valid (or zero for infinite)
 * @param validAfter first timestamp this UserOperation is valid
 */
    function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) {
        return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48));
    }

/**
 * keccak function over calldata.
 * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
 */
    function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
        assembly {
            let mem := mload(0x40)
            let len := data.length
            calldatacopy(mem, data.offset, len)
            ret := keccak256(mem, len)
        }
    }

Settings
{
  "remappings": [
    "account-abstraction/=lib/account-abstraction/contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "solady/=lib/solady/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"kernel","type":"address"},{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"disable","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ecdsaValidatorStorage","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"enable","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"validCaller","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"validateSignature","outputs":[{"internalType":"ValidationData","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"callGasLimit","type":"uint256"},{"internalType":"uint256","name":"verificationGasLimit","type":"uint256"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct UserOperation","name":"_userOp","type":"tuple"},{"internalType":"bytes32","name":"_userOpHash","type":"bytes32"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"ValidationData","name":"validationData","type":"uint256"}],"stateMutability":"payable","type":"function"}]

60808060405234610016576104ee908161001c8239f35b600080fdfe60406080815260048036101561001457600080fd5b600091823560e01c80630c959556146101d557806320709efc1461019b578063333daf921461015b5780633a871cdd1461010d578381638fc925aa146100c85750639ea9bd591461006457600080fd5b346100c457806003193601126100c45761007c610296565b9160243567ffffffffffffffff81116100c057936100a08392602096369101610263565b5050338152808552205490516001600160a01b0392831691909216148152f35b8480fd5b8280fd5b92905060203660031901126101095780359067ffffffffffffffff8211610104576100f591369101610263565b50503382528160205281205580f35b505050fd5b5050fd5b50600319836060368301126101545783359167ffffffffffffffff831161015757610160908336030112610154575060209261014d916024359101610348565b9051908152f35b80fd5b5080fd5b5082346101545781600319360112610154576024359067ffffffffffffffff8211610154575060209261019461014d9236908301610263565b9135610464565b838234610157576020366003190112610157576020916001600160a01b03908290826101c5610296565b1681528085522054169051908152f35b509060203660031901126100c45780359067ffffffffffffffff821161025f5761020191369101610263565b6014116100c4573560601c90338352826020528220805490826bffffffffffffffffffffffff60a01b831617905560018060a01b0316337f381c0d11398486654573703c51ee8210ce9461764d133f9f0e53b6a5397053318480a480f35b8380fd5b9181601f840112156102915782359167ffffffffffffffff8311610291576020838186019501011161029157565b600080fd5b600435906001600160a01b038216820361029157565b903590601e1981360301821215610291570180359067ffffffffffffffff82116102915760200191813603831361029157565b92919267ffffffffffffffff918281116103325760405192601f8201601f19908116603f011684019081118482101761033257604052829481845281830111610291578281602093846000960137010152565b634e487b7160e01b600052604160045260246000fd5b600092916001600160a01b039084833583811690819003610157578152806020528260408220541693826020527b19457468657265756d205369676e6564204d6573736167653a0a33328252603c60042091846103be6101408401946103b86103b187876102ac565b36916102df565b906103ee565b1686146103e557506103b16103d793926103b8926102ac565b16036103df57565b60019150565b96505050505050565b60207f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a19392608060405193838301516040526040830151928360605260416000988995869485526060840151851a885210915114165afa508251923d1561045757606052604052565b638baa579f90526004601cfd5b3360009081526020819052604081205490936001600160a01b03918216939192836104996104933685876102df565b836103ee565b1685146104e6576104d7926103b8916020527b19457468657265756d205369676e6564204d6573736167653a0a33328752603c6004209236916102df565b16036104e05790565b50600190565b50505050509056

Deployed Bytecode

0x60406080815260048036101561001457600080fd5b600091823560e01c80630c959556146101d557806320709efc1461019b578063333daf921461015b5780633a871cdd1461010d578381638fc925aa146100c85750639ea9bd591461006457600080fd5b346100c457806003193601126100c45761007c610296565b9160243567ffffffffffffffff81116100c057936100a08392602096369101610263565b5050338152808552205490516001600160a01b0392831691909216148152f35b8480fd5b8280fd5b92905060203660031901126101095780359067ffffffffffffffff8211610104576100f591369101610263565b50503382528160205281205580f35b505050fd5b5050fd5b50600319836060368301126101545783359167ffffffffffffffff831161015757610160908336030112610154575060209261014d916024359101610348565b9051908152f35b80fd5b5080fd5b5082346101545781600319360112610154576024359067ffffffffffffffff8211610154575060209261019461014d9236908301610263565b9135610464565b838234610157576020366003190112610157576020916001600160a01b03908290826101c5610296565b1681528085522054169051908152f35b509060203660031901126100c45780359067ffffffffffffffff821161025f5761020191369101610263565b6014116100c4573560601c90338352826020528220805490826bffffffffffffffffffffffff60a01b831617905560018060a01b0316337f381c0d11398486654573703c51ee8210ce9461764d133f9f0e53b6a5397053318480a480f35b8380fd5b9181601f840112156102915782359167ffffffffffffffff8311610291576020838186019501011161029157565b600080fd5b600435906001600160a01b038216820361029157565b903590601e1981360301821215610291570180359067ffffffffffffffff82116102915760200191813603831361029157565b92919267ffffffffffffffff918281116103325760405192601f8201601f19908116603f011684019081118482101761033257604052829481845281830111610291578281602093846000960137010152565b634e487b7160e01b600052604160045260246000fd5b600092916001600160a01b039084833583811690819003610157578152806020528260408220541693826020527b19457468657265756d205369676e6564204d6573736167653a0a33328252603c60042091846103be6101408401946103b86103b187876102ac565b36916102df565b906103ee565b1686146103e557506103b16103d793926103b8926102ac565b16036103df57565b60019150565b96505050505050565b60207f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a19392608060405193838301516040526040830151928360605260416000988995869485526060840151851a885210915114165afa508251923d1561045757606052604052565b638baa579f90526004601cfd5b3360009081526020819052604081205490936001600160a01b03918216939192836104996104933685876102df565b836103ee565b1685146104e6576104d7926103b8916020527b19457468657265756d205369676e6564204d6573736167653a0a33328752603c6004209236916102df565b16036104e05790565b50600190565b50505050509056

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.