Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 207104002 | 633 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
MultiChainValidator
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ECDSA} from "solady/utils/ECDSA.sol";
import {MerkleProofLib} from "solady/utils/MerkleProofLib.sol";
import {IValidator, IHook} from "../interfaces/IERC7579Modules.sol";
import {PackedUserOperation} from "../interfaces/PackedUserOperation.sol";
import {
SIG_VALIDATION_SUCCESS_UINT,
SIG_VALIDATION_FAILED_UINT,
MODULE_TYPE_VALIDATOR,
MODULE_TYPE_HOOK,
ERC1271_MAGICVALUE,
ERC1271_INVALID
} from "../types/Constants.sol";
struct ECDSAValidatorStorage {
address owner;
}
bytes constant DUMMY_ECDSA_SIG = hex"fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c";
contract MultiChainValidator is IValidator, IHook {
event OwnerRegistered(address indexed kernel, address indexed owner);
mapping(address => ECDSAValidatorStorage) public ecdsaValidatorStorage;
function onInstall(bytes calldata _data) external payable override {
if (_isInitialized(msg.sender)) revert AlreadyInitialized(msg.sender);
address owner = address(bytes20(_data[0:20]));
ecdsaValidatorStorage[msg.sender].owner = owner;
emit OwnerRegistered(msg.sender, owner);
}
function onUninstall(bytes calldata) external payable override {
if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
delete ecdsaValidatorStorage[msg.sender];
}
function isModuleType(uint256 typeID) external pure override returns (bool) {
return typeID == MODULE_TYPE_VALIDATOR || typeID == MODULE_TYPE_HOOK;
}
function isInitialized(address smartAccount) external view override returns (bool) {
return _isInitialized(smartAccount);
}
function _isInitialized(address smartAccount) internal view returns (bool) {
return ecdsaValidatorStorage[smartAccount].owner != address(0);
}
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
external
payable
override
returns (uint256)
{
bytes calldata sig = userOp.signature;
address owner = ecdsaValidatorStorage[msg.sender].owner;
if (sig.length == 65) {
// simple ecdsa verification
if (owner == ECDSA.recover(userOpHash, sig)) {
return SIG_VALIDATION_SUCCESS_UINT;
}
bytes32 ethHash = ECDSA.toEthSignedMessageHash(userOpHash);
address recovered = ECDSA.recover(ethHash, sig);
if (owner != recovered) {
return SIG_VALIDATION_FAILED_UINT;
}
return SIG_VALIDATION_SUCCESS_UINT;
}
bytes memory ecdsaSig = sig[0:65];
bytes32 merkleRoot = bytes32(sig[65:97]);
// if the signature is a dummy signature, then use dummyUserOpHash instead of real userOpHash
if (keccak256(ecdsaSig) == keccak256(DUMMY_ECDSA_SIG)) {
(bytes32 dummyUserOpHash, bytes32[] memory proof) = abi.decode(sig[97:], (bytes32, bytes32[]));
require(MerkleProofLib.verify(proof, merkleRoot, dummyUserOpHash), "hash is not in proof");
// otherwise, use real userOpHash
} else {
bytes32[] memory proof = abi.decode(sig[97:], (bytes32[]));
require(MerkleProofLib.verify(proof, merkleRoot, userOpHash), "hash is not in proof");
}
// simple ecdsa verification
if (owner == ECDSA.recover(merkleRoot, ecdsaSig)) {
return SIG_VALIDATION_SUCCESS_UINT;
}
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig);
if (owner != merkleRecovered) {
return SIG_VALIDATION_FAILED_UINT;
}
return SIG_VALIDATION_SUCCESS_UINT;
}
function isValidSignatureWithSender(address, bytes32 hash, bytes calldata sig)
external
view
override
returns (bytes4)
{
address owner = ecdsaValidatorStorage[msg.sender].owner;
if (sig.length == 65) {
// simple ecdsa verification
if (owner == ECDSA.recover(hash, sig)) {
return ERC1271_MAGICVALUE;
}
bytes32 ethHash = ECDSA.toEthSignedMessageHash(hash);
address recovered = ECDSA.recover(ethHash, sig);
if (owner != recovered) {
return ERC1271_INVALID;
}
return ERC1271_MAGICVALUE;
}
bytes memory ecdsaSig = sig[0:65];
bytes32 merkleRoot = bytes32(sig[65:97]);
bytes32[] memory proof = abi.decode(sig[97:], (bytes32[]));
require(MerkleProofLib.verify(proof, merkleRoot, hash), "hash is not in proof");
// simple ecdsa verification
if (owner == ECDSA.recover(merkleRoot, ecdsaSig)) {
return ERC1271_MAGICVALUE;
}
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig);
if (owner != merkleRecovered) {
return ERC1271_INVALID;
}
return ERC1271_MAGICVALUE;
}
function preCheck(address msgSender, uint256 value, bytes calldata)
external
payable
override
returns (bytes memory)
{
require(msgSender == ecdsaValidatorStorage[msg.sender].owner, "ECDSAValidator: sender is not owner");
return hex"";
}
function postCheck(bytes calldata hookData, bool success, bytes calldata res) external payable override {}
}// 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)
///
/// @dev Note:
/// - The recovery functions use the ecrecover precompile (0x1).
/// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
/// This is for more safety by default.
/// Use the `tryRecover` variants if you need to get the zero address back
/// upon recovery failure instead.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
/// See: https://eips.ethereum.org/EIPS/eip-2098
/// This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT use signatures as unique identifiers:
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
/// EIP-712 also enables readable signing of typed data for better user safety.
/// This implementation does NOT check if a signature is non-malleable.
library ECDSA {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The signature is invalid.
error InvalidSignature();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RECOVERY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
let m := mload(0x40) // Cache the free memory pointer.
for {} 1 {} {
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
if eq(mload(signature), 64) {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
break
}
if eq(mload(signature), 65) {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
break
}
result := 0
break
}
result :=
mload(
staticcall(
gas(), // Amount of gas left for the transaction.
result, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(returndatasize()) {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function recoverCalldata(bytes32 hash, bytes calldata signature)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
result := 1
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
for {} 1 {} {
if eq(signature.length, 64) {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
break
}
if eq(signature.length, 65) {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
break
}
result := 0
break
}
result :=
mload(
staticcall(
gas(), // Amount of gas left for the transaction.
result, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(returndatasize()) {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the EIP-2098 short form signature defined by `r` and `vs`.
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r)
mstore(0x60, shr(1, shl(1, vs))) // `s`.
result :=
mload(
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(returndatasize()) {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @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 {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, and(v, 0xff))
mstore(0x40, r)
mstore(0x60, s)
result :=
mload(
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(returndatasize()) {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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`.
function tryRecover(bytes32 hash, bytes memory signature)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
result := 1
let m := mload(0x40) // Cache the free memory pointer.
for {} 1 {} {
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
if eq(mload(signature), 64) {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
break
}
if eq(mload(signature), 65) {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
break
}
result := 0
break
}
pop(
staticcall(
gas(), // Amount of gas left for the transaction.
result, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x40, // Start of output.
0x20 // Size of output.
)
)
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
result := 1
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
for {} 1 {} {
if eq(signature.length, 64) {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
break
}
if eq(signature.length, 65) {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
break
}
result := 0
break
}
pop(
staticcall(
gas(), // Amount of gas left for the transaction.
result, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x40, // Start of output.
0x20 // Size of output.
)
)
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the EIP-2098 short form signature defined by `r` and `vs`.
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r)
mstore(0x60, shr(1, shl(1, vs))) // `s`.
pop(
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x40, // Start of output.
0x20 // Size of output.
)
)
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @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 {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, and(v, 0xff))
mstore(0x40, r)
mstore(0x60, s)
pop(
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x40, // Start of output.
0x20 // Size of output.
)
)
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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 {
mstore(0x20, hash) // Store into scratch space for keccak256.
mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 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.
/// Note: Supports lengths of `s` up to 999999 bytes.
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let sLength := mload(s)
let o := 0x20
mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
mstore(0x00, 0x00)
// Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
for { let temp := sLength } 1 {} {
o := sub(o, 1)
mstore8(o, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
// Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
mstore(s, sLength) // Restore the length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MERKLE PROOF VERIFICATION OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if mload(proof) {
// Initialize `offset` to the offset of `proof` elements in memory.
let offset := add(proof, 0x20)
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(offset, shl(5, mload(proof)))
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, mload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), mload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if proof.length {
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(proof.offset, shl(5, proof.length))
// Initialize `offset` to the offset of `proof` in the calldata.
let offset := proof.offset
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, calldataload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), calldataload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - The sum of the lengths of `proof` and `leaves` must never overflow.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The memory offset of `proof` must be non-zero
/// (i.e. `proof` is not pointing to the scratch space).
function verifyMultiProof(
bytes32[] memory proof,
bytes32 root,
bytes32[] memory leaves,
bool[] memory flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// Cache the lengths of the arrays.
let leavesLength := mload(leaves)
let proofLength := mload(proof)
let flagsLength := mload(flags)
// Advance the pointers of the arrays to point to the data.
leaves := add(0x20, leaves)
proof := add(0x20, proof)
flags := add(0x20, flags)
// If the number of flags is correct.
for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flagsLength) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof, shl(5, proofLength))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
leavesLength := shl(5, leavesLength)
for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
mstore(add(hashesFront, i), mload(add(leaves, i)))
}
// Compute the back of the hashes.
let hashesBack := add(hashesFront, leavesLength)
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flagsLength := add(hashesBack, shl(5, flagsLength))
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(mload(flags)) {
// Loads the next proof.
b := mload(proof)
proof := add(proof, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag.
flags := add(flags, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flagsLength)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof)
)
break
}
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The calldata offset of `proof` must be non-zero
/// (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
function verifyMultiProofCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32[] calldata leaves,
bool[] calldata flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// If the number of flags is correct.
for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flags.length) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
// forgefmt: disable-next-item
isValid := eq(
calldataload(
xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
),
root
)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof.offset, shl(5, proof.length))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
// Compute the back of the hashes.
let hashesBack := add(hashesFront, shl(5, leaves.length))
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flags.length := add(hashesBack, shl(5, flags.length))
// We don't need to make a copy of `proof.offset` or `flags.offset`,
// as they are pass-by-value (this trick may not always save gas).
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(calldataload(flags.offset)) {
// Loads the next proof.
b := calldataload(proof.offset)
proof.offset := add(proof.offset, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag offset.
flags.offset := add(flags.offset, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flags.length)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof.offset)
)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes32 array.
function emptyProof() internal pure returns (bytes32[] calldata proof) {
/// @solidity memory-safe-assembly
assembly {
proof.length := 0
}
}
/// @dev Returns an empty calldata bytes32 array.
function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
/// @solidity memory-safe-assembly
assembly {
leaves.length := 0
}
}
/// @dev Returns an empty calldata bool array.
function emptyFlags() internal pure returns (bool[] calldata flags) {
/// @solidity memory-safe-assembly
assembly {
flags.length := 0
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import {PackedUserOperation} from "./PackedUserOperation.sol";
interface IModule {
error AlreadyInitialized(address smartAccount);
error NotInitialized(address smartAccount);
/**
* @dev This function is called by the smart account during installation of the module
* @param data arbitrary data that may be required on the module during `onInstall`
* initialization
*
* MUST revert on error (i.e. if module is already enabled)
*/
function onInstall(bytes calldata data) external payable;
/**
* @dev This function is called by the smart account during uninstallation of the module
* @param data arbitrary data that may be required on the module during `onUninstall`
* de-initialization
*
* MUST revert on error
*/
function onUninstall(bytes calldata data) external payable;
/**
* @dev Returns boolean value if module is a certain type
* @param moduleTypeId the module type ID according the ERC-7579 spec
*
* MUST return true if the module is of the given type and false otherwise
*/
function isModuleType(uint256 moduleTypeId) external view returns (bool);
/**
* @dev Returns if the module was already initialized for a provided smartaccount
*/
function isInitialized(address smartAccount) external view returns (bool);
}
interface IValidator is IModule {
error InvalidTargetAddress(address target);
/**
* @dev Validates a transaction on behalf of the account.
* This function is intended to be called by the MSA during the ERC-4337 validaton phase
* Note: solely relying on bytes32 hash and signature is not sufficient for some
* validation implementations (i.e. SessionKeys often need access to userOp.calldata)
* @param userOp The user operation to be validated. The userOp MUST NOT contain any metadata.
* The MSA MUST clean up the userOp before sending it to the validator.
* @param userOpHash The hash of the user operation to be validated
* @return return value according to ERC-4337
*/
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
external
payable
returns (uint256);
/**
* Validator can be used for ERC-1271 validation
*/
function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data)
external
view
returns (bytes4);
}
interface IExecutor is IModule {}
interface IHook is IModule {
/**
* @dev Called by the smart account before execution
* @param msgSender the address that called the smart account
* @param value the value that was sent to the smart account
* @param msgData the data that was sent to the smart account
*
* MAY return arbitrary data in the `hookData` return value
*/
function preCheck(address msgSender, uint256 value, bytes calldata msgData)
external
payable
returns (bytes memory hookData);
/**
* @dev Called by the smart account after execution
* @param hookData the data that was returned by the `preCheck` function
* @param executionSuccess whether the execution(s) was (were) successful
* @param executionReturn the return/revert data of the execution(s)
*
* MAY validate the `hookData` to validate transaction context of the `preCheck` function
*/
function postCheck(bytes calldata hookData, bool executionSuccess, bytes calldata executionReturn)
external
payable;
}
interface IFallback is IModule {}
interface IPolicy is IModule {
function checkUserOpPolicy(bytes32 id, PackedUserOperation calldata userOp) external payable returns (uint256);
function checkSignaturePolicy(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
external
view
returns (uint256);
}
interface ISigner is IModule {
function checkUserOpSignature(bytes32 id, PackedUserOperation calldata userOp, bytes32 userOpHash)
external
payable
returns (uint256);
function checkSignature(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
external
view
returns (bytes4);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
/**
* 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 accountGasLimits - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
* @param preVerificationGas - Gas not calculated by the handleOps method, but added to the gas paid.
* Covers batch overhead.
* @param gasFees - packed gas fields maxFeePerGas and maxPriorityFeePerGas - Same as EIP-1559 gas parameter.
* @param paymasterAndData - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra 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 PackedUserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
bytes32 accountGasLimits;
uint256 preVerificationGas;
bytes32 gasFees; //maxPriorityFee and maxFeePerGas;
bytes paymasterAndData;
bytes signature;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {CallType, ExecType, ExecModeSelector} from "./Types.sol";
import {PassFlag, ValidationMode, ValidationType} from "./Types.sol";
import {ValidationData} from "./Types.sol";
// --- ERC7579 calltypes ---
// Default CallType
CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
// Batched CallType
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
CallType constant CALLTYPE_STATIC = CallType.wrap(0xFE);
// @dev Implementing delegatecall is OPTIONAL!
// implement delegatecall with extreme care.
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
// --- ERC7579 exectypes ---
// @dev default behavior is to revert on failure
// To allow very simple accounts to use mode encoding, the default behavior is to revert on failure
// Since this is value 0x00, no additional encoding is required for simple accounts
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
// @dev account may elect to change execution behavior. For example "try exec" / "allow fail"
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
// --- ERC7579 mode selector ---
ExecModeSelector constant EXEC_MODE_DEFAULT = ExecModeSelector.wrap(bytes4(0x00000000));
// --- Kernel permission skip flags ---
PassFlag constant SKIP_USEROP = PassFlag.wrap(0x0001);
PassFlag constant SKIP_SIGNATURE = PassFlag.wrap(0x0002);
// --- Kernel validation modes ---
ValidationMode constant VALIDATION_MODE_DEFAULT = ValidationMode.wrap(0x00);
ValidationMode constant VALIDATION_MODE_ENABLE = ValidationMode.wrap(0x01);
ValidationMode constant VALIDATION_MODE_INSTALL = ValidationMode.wrap(0x02);
// --- Kernel validation types ---
ValidationType constant VALIDATION_TYPE_ROOT = ValidationType.wrap(0x00);
ValidationType constant VALIDATION_TYPE_VALIDATOR = ValidationType.wrap(0x01);
ValidationType constant VALIDATION_TYPE_PERMISSION = ValidationType.wrap(0x02);
// --- storage slots ---
// bytes32(uint256(keccak256('kernel.v3.selector')) - 1)
bytes32 constant SELECTOR_MANAGER_STORAGE_SLOT = 0x7c341349a4360fdd5d5bc07e69f325dc6aaea3eb018b3e0ea7e53cc0bb0d6f3b;
// bytes32(uint256(keccak256('kernel.v3.executor')) - 1)
bytes32 constant EXECUTOR_MANAGER_STORAGE_SLOT = 0x1bbee3173dbdc223633258c9f337a0fff8115f206d302bea0ed3eac003b68b86;
// bytes32(uint256(keccak256('kernel.v3.hook')) - 1)
bytes32 constant HOOK_MANAGER_STORAGE_SLOT = 0x4605d5f70bb605094b2e761eccdc27bed9a362d8612792676bf3fb9b12832ffc;
// bytes32(uint256(keccak256('kernel.v3.validation')) - 1)
bytes32 constant VALIDATION_MANAGER_STORAGE_SLOT = 0x7bcaa2ced2a71450ed5a9a1b4848e8e5206dbc3f06011e595f7f55428cc6f84f;
bytes32 constant ERC1967_IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
// --- Kernel validation nonce incremental size limit ---
uint32 constant MAX_NONCE_INCREMENT_SIZE = 10;
// -- EIP712 type hash ---
bytes32 constant ENABLE_TYPE_HASH = 0xb17ab1224aca0d4255ef8161acaf2ac121b8faa32a4b2258c912cc5f8308c505;
bytes32 constant KERNEL_WRAPPER_TYPE_HASH = 0x1547321c374afde8a591d972a084b071c594c275e36724931ff96c25f2999c83;
// --- ERC constants ---
// ERC4337 constants
uint256 constant SIG_VALIDATION_FAILED_UINT = 1;
uint256 constant SIG_VALIDATION_SUCCESS_UINT = 0;
ValidationData constant SIG_VALIDATION_FAILED = ValidationData.wrap(SIG_VALIDATION_FAILED_UINT);
// ERC-1271 constants
bytes4 constant ERC1271_MAGICVALUE = 0x1626ba7e;
bytes4 constant ERC1271_INVALID = 0xffffffff;
uint256 constant MODULE_TYPE_VALIDATOR = 1;
uint256 constant MODULE_TYPE_EXECUTOR = 2;
uint256 constant MODULE_TYPE_FALLBACK = 3;
uint256 constant MODULE_TYPE_HOOK = 4;
uint256 constant MODULE_TYPE_POLICY = 5;
uint256 constant MODULE_TYPE_SIGNER = 6;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
// Custom type for improved developer experience
type ExecMode is bytes32;
type CallType is bytes1;
type ExecType is bytes1;
type ExecModeSelector is bytes4;
type ExecModePayload is bytes22;
using {eqModeSelector as ==} for ExecModeSelector global;
using {eqCallType as ==} for CallType global;
using {notEqCallType as !=} for CallType global;
using {eqExecType as ==} for ExecType global;
function eqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) == CallType.unwrap(b);
}
function notEqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) != CallType.unwrap(b);
}
function eqExecType(ExecType a, ExecType b) pure returns (bool) {
return ExecType.unwrap(a) == ExecType.unwrap(b);
}
function eqModeSelector(ExecModeSelector a, ExecModeSelector b) pure returns (bool) {
return ExecModeSelector.unwrap(a) == ExecModeSelector.unwrap(b);
}
type ValidationMode is bytes1;
type ValidationId is bytes21;
type ValidationType is bytes1;
type PermissionId is bytes4;
type PolicyData is bytes22; // 2bytes for flag on skip, 20 bytes for validator address
type PassFlag is bytes2;
using {vModeEqual as ==} for ValidationMode global;
using {vTypeEqual as ==} for ValidationType global;
using {vIdentifierEqual as ==} for ValidationId global;
using {vModeNotEqual as !=} for ValidationMode global;
using {vTypeNotEqual as !=} for ValidationType global;
using {vIdentifierNotEqual as !=} for ValidationId global;
// nonce = uint192(key) + nonce
// key = mode + (vtype + validationDataWithoutType) + 2bytes parallelNonceKey
// key = 0x00 + 0x00 + 0x000 .. 00 + 0x0000
// key = 0x00 + 0x01 + 0x1234...ff + 0x0000
// key = 0x00 + 0x02 + ( ) + 0x000
function vModeEqual(ValidationMode a, ValidationMode b) pure returns (bool) {
return ValidationMode.unwrap(a) == ValidationMode.unwrap(b);
}
function vModeNotEqual(ValidationMode a, ValidationMode b) pure returns (bool) {
return ValidationMode.unwrap(a) != ValidationMode.unwrap(b);
}
function vTypeEqual(ValidationType a, ValidationType b) pure returns (bool) {
return ValidationType.unwrap(a) == ValidationType.unwrap(b);
}
function vTypeNotEqual(ValidationType a, ValidationType b) pure returns (bool) {
return ValidationType.unwrap(a) != ValidationType.unwrap(b);
}
function vIdentifierEqual(ValidationId a, ValidationId b) pure returns (bool) {
return ValidationId.unwrap(a) == ValidationId.unwrap(b);
}
function vIdentifierNotEqual(ValidationId a, ValidationId b) pure returns (bool) {
return ValidationId.unwrap(a) != ValidationId.unwrap(b);
}
type ValidationData is uint256;
type ValidAfter is uint48;
type ValidUntil is uint48;
function getValidationResult(ValidationData validationData) pure returns (address result) {
assembly {
result := validationData
}
}
function packValidationData(ValidAfter validAfter, ValidUntil validUntil) pure returns (uint256) {
return uint256(ValidAfter.unwrap(validAfter)) << 208 | uint256(ValidUntil.unwrap(validUntil)) << 160;
}
function parseValidationData(uint256 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)
}
}{
"remappings": [
"ExcessivelySafeCall/=lib/ExcessivelySafeCall/src/",
"forge-std/=lib/forge-std/src/",
"solady/=lib/solady/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": false
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"InvalidTargetAddress","type":"error"},{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"NotInitialized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"kernel","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"OwnerRegistered","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ecdsaValidatorStorage","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"typeID","type":"uint256"}],"name":"isModuleType","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"sig","type":"bytes"}],"name":"isValidSignatureWithSender","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onInstall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"onUninstall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"hookData","type":"bytes"},{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"res","type":"bytes"}],"name":"postCheck","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"preCheck","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","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":"bytes32","name":"accountGasLimits","type":"bytes32"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"bytes32","name":"gasFees","type":"bytes32"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct PackedUserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b50610f08806100206000396000f3fe6080604052600436106100865760003560e01c8063aacbd72a11610059578063aacbd72a14610127578063d60b347f1461013c578063d68f60251461016c578063ecd059611461018c578063f551e2ee146101ac57600080fd5b806320709efc1461008b5780636d61fe70146100de5780638a91b0e3146100f35780639700320314610106575b600080fd5b34801561009757600080fd5b506100c16100a6366004610a60565b6000602081905290815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100f16100ec366004610acb565b6101e5565b005b6100f1610101366004610acb565b610291565b610119610114366004610b0d565b6102ea565b6040519081526020016100d5565b6100f1610135366004610b58565b5050505050565b34801561014857600080fd5b5061015c610157366004610a60565b610603565b60405190151581526020016100d5565b61017f61017a366004610be2565b610627565b6040516100d59190610c3c565b34801561019857600080fd5b5061015c6101a7366004610c8b565b6106b9565b3480156101b857600080fd5b506101cc6101c7366004610be2565b6106cc565b6040516001600160e01b031990911681526020016100d5565b336000908152602081905260409020546001600160a01b031615610223576040516393360fbf60e01b81523360048201526024015b60405180910390fd5b60006102326014828486610ca4565b61023b91610cce565b3360008181526020819052604080822080546001600160a01b03191660609590951c94851790555192935083927fa5e1f8b4009110f5525798d04ae2125421a12d0590aa52c13682ff1bd3c492ca9190a3505050565b336000908152602081905260409020546001600160a01b03166102c95760405163f91bd6f160e01b815233600482015260240161021a565b505033600090815260208190526040902080546001600160a01b0319169055565b600036816102fc610100860186610d03565b3360009081526020819052604090205491935091506001600160a01b03166041829003610433576103638584848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061097192505050565b6001600160a01b0316816001600160a01b03160361038757600093505050506105fd565b60006103b8866020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b905060006103fc8286868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061097192505050565b9050806001600160a01b0316836001600160a01b031614610425576001955050505050506105fd565b6000955050505050506105fd565b60006104426041828587610ca4565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250939450610489925060619150604190508688610ca4565b61049291610d4a565b9050604051806080016040528060418152602001610ec76041913980519060200120828051906020012003610510576000806104d1866061818a610ca4565b8101906104de9190610e1c565b915091506104ed818484610a02565b6105095760405162461bcd60e51b815260040161021a90610e63565b5050610557565b600061051f8560618189610ca4565b81019061052c9190610e91565b905061053981838a610a02565b6105555760405162461bcd60e51b815260040161021a90610e63565b505b6105618183610971565b6001600160a01b0316836001600160a01b031603610587576000955050505050506105fd565b60006105b8826020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b905060006105c68285610971565b9050806001600160a01b0316856001600160a01b0316146105f15760019750505050505050506105fd565b60009750505050505050505b92915050565b6001600160a01b0380821660009081526020819052604081205490911615156105fd565b336000908152602081905260409020546060906001600160a01b038681169116146106a05760405162461bcd60e51b815260206004820152602360248201527f454344534156616c696461746f723a2073656e646572206973206e6f74206f776044820152623732b960e91b606482015260840161021a565b506040805160208101909152600081525b949350505050565b600060018214806105fd57505060041490565b336000908152602081905260408120546001600160a01b0316604183900361080d5761072e8585858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061097192505050565b6001600160a01b0316816001600160a01b0316036107565750630b135d3f60e11b90506106b1565b6000610787866020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b905060006107cb8287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061097192505050565b9050806001600160a01b0316836001600160a01b0316146107fa57506001600160e01b031992506106b1915050565b50630b135d3f60e11b92506106b1915050565b600061081c6041828688610ca4565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250939450610863925060619150604190508789610ca4565b61086c91610d4a565b9050600061087d866061818a610ca4565b81019061088a9190610e91565b905061089781838a610a02565b6108b35760405162461bcd60e51b815260040161021a90610e63565b6108bd8284610971565b6001600160a01b0316846001600160a01b0316036108e95750630b135d3f60e11b93506106b192505050565b600061091a836020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b905060006109288286610971565b9050806001600160a01b0316866001600160a01b03161461095a57506001600160e01b031995506106b1945050505050565b50630b135d3f60e11b9a9950505050505050505050565b60405160019083600052602083015160405260408351036109ad57604083015160ff81901c601b016020526001600160ff1b03166060526109d3565b60418351036109ce57606083015160001a60205260408301516060526109d3565b600091505b6020600160806000855afa5191503d6109f457638baa579f6000526004601cfd5b600060605260405292915050565b6000835115610a3d5760208401845160051b81015b8151841160051b938452815160209485185260406000209390910190808210610a175750505b5014919050565b80356001600160a01b0381168114610a5b57600080fd5b919050565b600060208284031215610a7257600080fd5b610a7b82610a44565b9392505050565b60008083601f840112610a9457600080fd5b50813567ffffffffffffffff811115610aac57600080fd5b602083019150836020828501011115610ac457600080fd5b9250929050565b60008060208385031215610ade57600080fd5b823567ffffffffffffffff811115610af557600080fd5b610b0185828601610a82565b90969095509350505050565b60008060408385031215610b2057600080fd5b823567ffffffffffffffff811115610b3757600080fd5b83016101208186031215610b4a57600080fd5b946020939093013593505050565b600080600080600060608688031215610b7057600080fd5b853567ffffffffffffffff80821115610b8857600080fd5b610b9489838a01610a82565b9097509550602088013591508115158214610bae57600080fd5b90935060408701359080821115610bc457600080fd5b50610bd188828901610a82565b969995985093965092949392505050565b60008060008060608587031215610bf857600080fd5b610c0185610a44565b935060208501359250604085013567ffffffffffffffff811115610c2457600080fd5b610c3087828801610a82565b95989497509550505050565b60006020808352835180602085015260005b81811015610c6a57858101830151858201604001528201610c4e565b506000604082860101526040601f19601f8301168501019250505092915050565b600060208284031215610c9d57600080fd5b5035919050565b60008085851115610cb457600080fd5b83861115610cc157600080fd5b5050820193919092039150565b6bffffffffffffffffffffffff198135818116916014851015610cfb5780818660140360031b1b83161692505b505092915050565b6000808335601e19843603018112610d1a57600080fd5b83018035915067ffffffffffffffff821115610d3557600080fd5b602001915036819003821315610ac457600080fd5b803560208310156105fd57600019602084900360031b1b1692915050565b634e487b7160e01b600052604160045260246000fd5b600082601f830112610d8f57600080fd5b8135602067ffffffffffffffff80831115610dac57610dac610d68565b8260051b604051601f19603f83011681018181108482111715610dd157610dd1610d68565b6040529384526020818701810194908101925087851115610df157600080fd5b6020870191505b84821015610e1157813583529183019190830190610df8565b979650505050505050565b60008060408385031215610e2f57600080fd5b82359150602083013567ffffffffffffffff811115610e4d57600080fd5b610e5985828601610d7e565b9150509250929050565b6020808252601490820152733430b9b41034b9903737ba1034b710383937b7b360611b604082015260600190565b600060208284031215610ea357600080fd5b813567ffffffffffffffff811115610eba57600080fd5b6106b184828501610d7e56fefffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
Deployed Bytecode
0x6080604052600436106100865760003560e01c8063aacbd72a11610059578063aacbd72a14610127578063d60b347f1461013c578063d68f60251461016c578063ecd059611461018c578063f551e2ee146101ac57600080fd5b806320709efc1461008b5780636d61fe70146100de5780638a91b0e3146100f35780639700320314610106575b600080fd5b34801561009757600080fd5b506100c16100a6366004610a60565b6000602081905290815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100f16100ec366004610acb565b6101e5565b005b6100f1610101366004610acb565b610291565b610119610114366004610b0d565b6102ea565b6040519081526020016100d5565b6100f1610135366004610b58565b5050505050565b34801561014857600080fd5b5061015c610157366004610a60565b610603565b60405190151581526020016100d5565b61017f61017a366004610be2565b610627565b6040516100d59190610c3c565b34801561019857600080fd5b5061015c6101a7366004610c8b565b6106b9565b3480156101b857600080fd5b506101cc6101c7366004610be2565b6106cc565b6040516001600160e01b031990911681526020016100d5565b336000908152602081905260409020546001600160a01b031615610223576040516393360fbf60e01b81523360048201526024015b60405180910390fd5b60006102326014828486610ca4565b61023b91610cce565b3360008181526020819052604080822080546001600160a01b03191660609590951c94851790555192935083927fa5e1f8b4009110f5525798d04ae2125421a12d0590aa52c13682ff1bd3c492ca9190a3505050565b336000908152602081905260409020546001600160a01b03166102c95760405163f91bd6f160e01b815233600482015260240161021a565b505033600090815260208190526040902080546001600160a01b0319169055565b600036816102fc610100860186610d03565b3360009081526020819052604090205491935091506001600160a01b03166041829003610433576103638584848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061097192505050565b6001600160a01b0316816001600160a01b03160361038757600093505050506105fd565b60006103b8866020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b905060006103fc8286868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061097192505050565b9050806001600160a01b0316836001600160a01b031614610425576001955050505050506105fd565b6000955050505050506105fd565b60006104426041828587610ca4565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250939450610489925060619150604190508688610ca4565b61049291610d4a565b9050604051806080016040528060418152602001610ec76041913980519060200120828051906020012003610510576000806104d1866061818a610ca4565b8101906104de9190610e1c565b915091506104ed818484610a02565b6105095760405162461bcd60e51b815260040161021a90610e63565b5050610557565b600061051f8560618189610ca4565b81019061052c9190610e91565b905061053981838a610a02565b6105555760405162461bcd60e51b815260040161021a90610e63565b505b6105618183610971565b6001600160a01b0316836001600160a01b031603610587576000955050505050506105fd565b60006105b8826020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b905060006105c68285610971565b9050806001600160a01b0316856001600160a01b0316146105f15760019750505050505050506105fd565b60009750505050505050505b92915050565b6001600160a01b0380821660009081526020819052604081205490911615156105fd565b336000908152602081905260409020546060906001600160a01b038681169116146106a05760405162461bcd60e51b815260206004820152602360248201527f454344534156616c696461746f723a2073656e646572206973206e6f74206f776044820152623732b960e91b606482015260840161021a565b506040805160208101909152600081525b949350505050565b600060018214806105fd57505060041490565b336000908152602081905260408120546001600160a01b0316604183900361080d5761072e8585858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061097192505050565b6001600160a01b0316816001600160a01b0316036107565750630b135d3f60e11b90506106b1565b6000610787866020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b905060006107cb8287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061097192505050565b9050806001600160a01b0316836001600160a01b0316146107fa57506001600160e01b031992506106b1915050565b50630b135d3f60e11b92506106b1915050565b600061081c6041828688610ca4565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250939450610863925060619150604190508789610ca4565b61086c91610d4a565b9050600061087d866061818a610ca4565b81019061088a9190610e91565b905061089781838a610a02565b6108b35760405162461bcd60e51b815260040161021a90610e63565b6108bd8284610971565b6001600160a01b0316846001600160a01b0316036108e95750630b135d3f60e11b93506106b192505050565b600061091a836020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b905060006109288286610971565b9050806001600160a01b0316866001600160a01b03161461095a57506001600160e01b031995506106b1945050505050565b50630b135d3f60e11b9a9950505050505050505050565b60405160019083600052602083015160405260408351036109ad57604083015160ff81901c601b016020526001600160ff1b03166060526109d3565b60418351036109ce57606083015160001a60205260408301516060526109d3565b600091505b6020600160806000855afa5191503d6109f457638baa579f6000526004601cfd5b600060605260405292915050565b6000835115610a3d5760208401845160051b81015b8151841160051b938452815160209485185260406000209390910190808210610a175750505b5014919050565b80356001600160a01b0381168114610a5b57600080fd5b919050565b600060208284031215610a7257600080fd5b610a7b82610a44565b9392505050565b60008083601f840112610a9457600080fd5b50813567ffffffffffffffff811115610aac57600080fd5b602083019150836020828501011115610ac457600080fd5b9250929050565b60008060208385031215610ade57600080fd5b823567ffffffffffffffff811115610af557600080fd5b610b0185828601610a82565b90969095509350505050565b60008060408385031215610b2057600080fd5b823567ffffffffffffffff811115610b3757600080fd5b83016101208186031215610b4a57600080fd5b946020939093013593505050565b600080600080600060608688031215610b7057600080fd5b853567ffffffffffffffff80821115610b8857600080fd5b610b9489838a01610a82565b9097509550602088013591508115158214610bae57600080fd5b90935060408701359080821115610bc457600080fd5b50610bd188828901610a82565b969995985093965092949392505050565b60008060008060608587031215610bf857600080fd5b610c0185610a44565b935060208501359250604085013567ffffffffffffffff811115610c2457600080fd5b610c3087828801610a82565b95989497509550505050565b60006020808352835180602085015260005b81811015610c6a57858101830151858201604001528201610c4e565b506000604082860101526040601f19601f8301168501019250505092915050565b600060208284031215610c9d57600080fd5b5035919050565b60008085851115610cb457600080fd5b83861115610cc157600080fd5b5050820193919092039150565b6bffffffffffffffffffffffff198135818116916014851015610cfb5780818660140360031b1b83161692505b505092915050565b6000808335601e19843603018112610d1a57600080fd5b83018035915067ffffffffffffffff821115610d3557600080fd5b602001915036819003821315610ac457600080fd5b803560208310156105fd57600019602084900360031b1b1692915050565b634e487b7160e01b600052604160045260246000fd5b600082601f830112610d8f57600080fd5b8135602067ffffffffffffffff80831115610dac57610dac610d68565b8260051b604051601f19603f83011681018181108482111715610dd157610dd1610d68565b6040529384526020818701810194908101925087851115610df157600080fd5b6020870191505b84821015610e1157813583529183019190830190610df8565b979650505050505050565b60008060408385031215610e2f57600080fd5b82359150602083013567ffffffffffffffff811115610e4d57600080fd5b610e5985828601610d7e565b9150509250929050565b6020808252601490820152733430b9b41034b9903737ba1034b710383937b7b360611b604082015260600190565b600060208284031215610ea357600080fd5b813567ffffffffffffffff811115610eba57600080fd5b6106b184828501610d7e56fefffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.