Latest 17 from a total of 17 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Transfer | 18510645 | 848 days ago | IN | 0.01273691 ETH | 0.00067732 | ||||
| Transfer | 18507225 | 848 days ago | IN | 0.03 ETH | 0.00068027 | ||||
| Transfer | 18503239 | 849 days ago | IN | 0.02 ETH | 0.00033501 | ||||
| Transfer | 18502447 | 849 days ago | IN | 0.02 ETH | 0.00040013 | ||||
| Transfer | 18402968 | 863 days ago | IN | 0.025 ETH | 0.000173 | ||||
| Transfer | 18381967 | 866 days ago | IN | 0.01 ETH | 0.00015411 | ||||
| Transfer | 18381786 | 866 days ago | IN | 0.01 ETH | 0.00014638 | ||||
| Transfer | 18317268 | 875 days ago | IN | 0.033 ETH | 0.00014853 | ||||
| Transfer | 18314464 | 875 days ago | IN | 0.05 ETH | 0.00037667 | ||||
| Transfer | 18245393 | 885 days ago | IN | 0.0167 ETH | 0.0001765 | ||||
| Transfer | 18182124 | 893 days ago | IN | 0.02 ETH | 0.00027391 | ||||
| Transfer | 18167508 | 896 days ago | IN | 0.00957179 ETH | 0.0001905 | ||||
| Transfer | 18157679 | 897 days ago | IN | 0.036 ETH | 0.00034789 | ||||
| Transfer | 18114000 | 903 days ago | IN | 0.15 ETH | 0.00107384 | ||||
| Transfer | 18085561 | 907 days ago | IN | 0.1 ETH | 0.00051723 | ||||
| Transfer | 18066741 | 910 days ago | IN | 0.05 ETH | 0.00032281 | ||||
| Transfer | 18014428 | 917 days ago | IN | 0.069 ETH | 0.00070997 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 19536712 | 704 days ago | 0.00095332 ETH | ||||
| Transfer | 18818369 | 804 days ago | 0.018245 ETH | ||||
| Transfer | 18509785 | 848 days ago | 0.00696891 ETH | ||||
| Transfer* | 18508198 | 848 days ago | 0.03 ETH | ||||
| Transfer* | 18503415 | 849 days ago | 0.02 ETH | ||||
| Transfer* | 18502549 | 849 days ago | 0.0193 ETH | ||||
| Transfer | 18502452 | 849 days ago | 0.0175 ETH | ||||
| Transfer | 18502452 | 849 days ago | 0.00061353 ETH | ||||
| Transfer* | 18431642 | 859 days ago | 0.015 ETH | ||||
| Transfer* | 18415292 | 861 days ago | 0.01 ETH | ||||
| Transfer | 18381970 | 866 days ago | 0.01 ETH | ||||
| Transfer | 18381901 | 866 days ago | 0.01 ETH | ||||
| Transfer | 18381655 | 866 days ago | 0.01378364 ETH | ||||
| Transfer | 18381629 | 866 days ago | 1 wei | ||||
| Transfer | 18381350 | 866 days ago | 1 wei | ||||
| Transfer | 18379586 | 866 days ago | 1 wei | ||||
| Deposit | 18323421 | 874 days ago | 0.015 ETH | ||||
| Transfer | 18317286 | 875 days ago | 0.00578364 ETH | ||||
| Deposit | 18317274 | 875 days ago | 0.01 ETH | ||||
| Deposit | 18316362 | 875 days ago | 0.02537282 ETH | ||||
| Deposit | 18316255 | 875 days ago | 0.01 ETH | ||||
| Deposit | 18315001 | 875 days ago | 0.01816 ETH | ||||
| Safe Transfer ET... | 18315001 | 875 days ago | 0.00184 ETH | ||||
| Safe Transfer ET... | 18245886 | 885 days ago | 0.0018 ETH | ||||
| Transfer | 18245886 | 885 days ago | 0.00717282 ETH |
Loading...
Loading
Loading...
Loading
Loading...
Loading
Minimal Proxy Contract for 0xf048ad83cb2dfd6037a43902a2a5be04e53cd2eb
Contract Name:
Kernel
Compiler Version
v0.8.19+commit.7dd6d404
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;
// Importing external libraries and contracts
import "solady/utils/EIP712.sol";
import "solady/utils/ECDSA.sol";
import "account-abstraction/interfaces/IEntryPoint.sol";
import "./abstract/Compatibility.sol";
import "./abstract/KernelStorage.sol";
import "./utils/KernelHelper.sol";
import "src/common/Constants.sol";
import "src/common/Enum.sol";
/// @title Kernel
/// @author taek<leekt216@gmail.com>
/// @notice wallet kernel for extensible wallet functionality
contract Kernel is EIP712, Compatibility, KernelStorage {
string public constant name = KERNEL_NAME;
string public constant version = KERNEL_VERSION;
error NotEntryPoint();
error DisabledMode();
/// @dev Sets up the EIP712 and KernelStorage with the provided entry point
constructor(IEntryPoint _entryPoint) KernelStorage(_entryPoint) {}
function _domainNameAndVersion() internal pure override returns (string memory, string memory) {
return (KERNEL_NAME, KERNEL_VERSION);
}
/// @notice Accepts incoming Ether transactions and calls from the EntryPoint contract
/// @dev This function will delegate any call to the appropriate executor based on the function signature.
fallback() external payable {
bytes4 sig = msg.sig;
address executor = getKernelStorage().execution[sig].executor;
if (msg.sender != address(entryPoint) && !_checkCaller()) {
revert NotAuthorizedCaller();
}
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), executor, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
/// @notice Executes a function call to an external contract
/// @dev The type of operation (call or delegatecall) is specified as an argument.
/// @param to The address of the target contract
/// @param value The amount of Ether to send
/// @param data The call data to be sent
/// @param operation The type of operation (call or delegatecall)
function execute(address to, uint256 value, bytes memory data, Operation operation) external payable {
if (msg.sender != address(entryPoint) && !_checkCaller()) {
revert NotAuthorizedCaller();
}
if (operation == Operation.Call) {
assembly {
let success := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
returndatacopy(0, 0, returndatasize())
switch success
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
} else {
assembly {
let success := delegatecall(gas(), to, add(data, 0x20), mload(data), 0, 0)
returndatacopy(0, 0, returndatasize())
switch success
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}
/// @notice Validates a user operation based on its mode
/// @dev This function will validate user operation and be called by EntryPoint
/// @param userOp The user operation to be validated
/// @param userOpHash The hash of the user operation
/// @param missingAccountFunds The funds needed to be reimbursed
/// @return validationData The data used for validation
function validateUserOp(UserOperation memory userOp, bytes32 userOpHash, uint256 missingAccountFunds)
external
payable
returns (ValidationData validationData)
{
if (msg.sender != address(entryPoint)) {
revert NotEntryPoint();
}
bytes calldata userOpSignature;
uint256 userOpEndOffset;
bytes32 storage_slot_1;
assembly {
userOpEndOffset := add(calldataload(0x04), 0x24)
userOpSignature.offset := add(calldataload(add(userOpEndOffset, 0x120)), userOpEndOffset)
userOpSignature.length := calldataload(sub(userOpSignature.offset, 0x20))
storage_slot_1 := sload(KERNEL_STORAGE_SLOT_1)
}
// mode based signature
bytes4 mode = bytes4(userOpSignature[0:4]); // mode == 00..00 use validators
// mode == 0x00000000 use sudo validator
// mode == 0x00000001 use given validator
// mode == 0x00000002 enable validator
IKernelValidator validator;
if (mode == 0x00000000) {
// sudo mode (use default validator)
userOpSignature = userOpSignature[4:];
assembly {
validator := shr(80, storage_slot_1)
}
} else if (mode & (storage_slot_1 << 224) != 0x00000000) {
revert DisabledMode();
} else if (mode == 0x00000001) {
bytes calldata userOpCallData;
assembly {
userOpCallData.offset := add(calldataload(add(userOpEndOffset, 0x40)), userOpEndOffset)
userOpCallData.length := calldataload(sub(userOpCallData.offset, 0x20))
}
ExecutionDetail storage detail = getKernelStorage().execution[bytes4(userOpCallData[0:4])];
validator = detail.validator;
if (address(validator) == address(0)) {
assembly {
validator := shr(80, storage_slot_1)
}
}
userOpSignature = userOpSignature[4:];
validationData = packValidationData(detail.validAfter, detail.validUntil);
} else if (mode == 0x00000002) {
bytes calldata userOpCallData;
assembly {
userOpCallData.offset := add(calldataload(add(userOpEndOffset, 0x40)), userOpEndOffset)
userOpCallData.length := calldataload(sub(userOpCallData.offset, 0x20))
}
// use given validator
// userOpSignature[4:10] = validAfter,
// userOpSignature[10:16] = validUntil,
// userOpSignature[16:36] = validator address,
(validator, validationData, userOpSignature) =
_approveValidator(bytes4(userOpCallData[0:4]), userOpSignature);
} else {
return SIG_VALIDATION_FAILED;
}
if (missingAccountFunds != 0) {
assembly {
pop(call(gas(), caller(), missingAccountFunds, 0, 0, 0, 0))
}
//ignore failure (its EntryPoint's job to verify, not account.)
}
userOp.signature = userOpSignature;
validationData =
_intersectValidationData(validationData, validator.validateUserOp(userOp, userOpHash, missingAccountFunds));
return validationData;
}
function _approveValidator(bytes4 sig, bytes calldata signature)
internal
returns (IKernelValidator validator, ValidationData validationData, bytes calldata validationSig)
{
unchecked {
validator = IKernelValidator(address(bytes20(signature[16:36])));
uint256 cursor = 88;
uint256 length = uint256(bytes32(signature[56:88])); // this is enableDataLength
bytes calldata enableData;
assembly {
enableData.offset := add(signature.offset, cursor)
enableData.length := length
cursor := add(cursor, length) // 88 + enableDataLength
}
length = uint256(bytes32(signature[cursor:cursor + 32])); // this is enableSigLength
assembly {
cursor := add(cursor, 32)
}
bytes32 enableDigest = _hashTypedData(
keccak256(
abi.encode(
VALIDATOR_APPROVED_STRUCT_HASH,
bytes4(sig),
uint256(bytes32(signature[4:36])),
address(bytes20(signature[36:56])),
keccak256(enableData)
)
)
);
validationData = _intersectValidationData(
getKernelStorage().defaultValidator.validateSignature(enableDigest, signature[cursor:cursor + length]),
ValidationData.wrap(
uint256(bytes32(signature[4:36]))
& 0xffffffffffffffffffffffff0000000000000000000000000000000000000000
)
);
assembly {
cursor := add(cursor, length)
validationSig.offset := add(signature.offset, cursor)
validationSig.length := sub(signature.length, cursor)
}
getKernelStorage().execution[sig] = ExecutionDetail({
validAfter: ValidAfter.wrap(uint48(bytes6(signature[4:10]))),
validUntil: ValidUntil.wrap(uint48(bytes6(signature[10:16]))),
executor: address(bytes20(signature[36:56])),
validator: IKernelValidator(address(bytes20(signature[16:36])))
});
validator.enable(enableData);
}
}
/// @notice Checks if a signature is valid
/// @dev This function checks if a signature is valid based on the hash of the data signed.
/// @param hash The hash of the data that was signed
/// @param signature The signature to be validated
/// @return The magic value 0x1626ba7e if the signature is valid, otherwise returns 0xffffffff.
function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
ValidationData validationData = getKernelStorage().defaultValidator.validateSignature(hash, signature);
(ValidAfter validAfter, ValidUntil validUntil, address result) = parseValidationData(validationData);
if (ValidAfter.unwrap(validAfter) > block.timestamp) {
return 0xffffffff;
}
if (ValidUntil.unwrap(validUntil) < block.timestamp) {
return 0xffffffff;
}
if (result != address(0)) {
return 0xffffffff;
}
return 0x1626ba7e;
}
function _checkCaller() internal view returns (bool) {
if (getKernelStorage().defaultValidator.validCaller(msg.sender, msg.data)) {
return true;
}
bytes4 sig = msg.sig;
ExecutionDetail storage detail = getKernelStorage().execution[sig];
if (
address(detail.validator) == address(0)
|| (ValidUntil.unwrap(detail.validUntil) != 0 && ValidUntil.unwrap(detail.validUntil) < block.timestamp)
|| ValidAfter.unwrap(detail.validAfter) > block.timestamp
) {
return false;
} else {
return detail.validator.validCaller(msg.sender, msg.data);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
/// Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS AND IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 internal constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
address private immutable _cachedThis;
uint256 private immutable _cachedChainId;
bytes32 private immutable _cachedNameHash;
bytes32 private immutable _cachedVersionHash;
bytes32 private immutable _cachedDomainSeparator;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Cache the hashes for cheaper runtime gas costs.
/// In the case of upgradeable contracts (i.e. proxies),
/// or if the chain id changes due to a hard fork,
/// the domain separator will be seamlessly calculated on-the-fly.
constructor() {
_cachedThis = address(this);
_cachedChainId = block.chainid;
(string memory name, string memory version) = _domainNameAndVersion();
bytes32 nameHash = keccak256(bytes(name));
bytes32 versionHash = keccak256(bytes(version));
_cachedNameHash = nameHash;
_cachedVersionHash = versionHash;
bytes32 separator;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
_cachedDomainSeparator = separator;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to return the domain name and version.
/// ```
/// function _domainNameAndVersion()
/// internal
/// pure
/// virtual
/// returns (string memory name, string memory version)
/// {
/// name = "Solady";
/// version = "1";
/// }
/// ```
function _domainNameAndVersion()
internal
pure
virtual
returns (string memory name, string memory version);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _domainSeparator() internal view virtual returns (bytes32 separator) {
separator = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) {
separator = _buildDomainSeparator();
}
}
/// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
/// given `structHash`, as defined in
/// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
///
/// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
/// ```
/// bytes32 digest = _hashTypedData(keccak256(abi.encode(
/// keccak256("Mail(address to,string contents)"),
/// mailTo,
/// keccak256(bytes(mailContents))
/// )));
/// address signer = ECDSA.recover(digest, signature);
/// ```
function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
bytes32 separator = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) {
separator = _buildDomainSeparator();
}
/// @solidity memory-safe-assembly
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, separator) // Store the domain separator.
mstore(0x3a, structHash) // Store the struct hash.
digest := keccak256(0x18, 0x42)
// Restore the part of the free memory slot that was overwritten.
mstore(0x3a, 0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-5267 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev See: https://eips.ethereum.org/EIPS/eip-5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b01111`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
salt = salt; // `bytes32(0)`.
extensions = extensions; // `new uint256[](0)`.
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _buildDomainSeparator() private view returns (bytes32 separator) {
bytes32 nameHash = _cachedNameHash;
bytes32 versionHash = _cachedVersionHash;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
/// @dev Returns if the cached domain separator has been invalidated.
function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
uint256 cachedChainId = _cachedChainId;
address cachedThis = _cachedThis;
/// @solidity memory-safe-assembly
assembly {
result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
}
}
}// 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
}
}
}/**
** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
** Only one instance required on each chain.
**/
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
import "./UserOperation.sol";
import "./IStakeManager.sol";
import "./IAggregator.sol";
import "./INonceManager.sol";
interface IEntryPoint is IStakeManager, INonceManager {
/***
* An event emitted after each successful request
* @param userOpHash - unique identifier for the request (hash its entire content, except signature).
* @param sender - the account that generates this request.
* @param paymaster - if non-null, the paymaster that pays for this request.
* @param nonce - the nonce value from the request.
* @param success - true if the sender transaction succeeded, false if reverted.
* @param actualGasCost - actual amount paid (by account or paymaster) for this UserOperation.
* @param actualGasUsed - total gas used by this UserOperation (including preVerification, creation, validation and execution).
*/
event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed);
/**
* account "sender" was deployed.
* @param userOpHash the userOp that deployed this account. UserOperationEvent will follow.
* @param sender the account that is deployed
* @param factory the factory used to deploy this account (in the initCode)
* @param paymaster the paymaster used by this UserOp
*/
event AccountDeployed(bytes32 indexed userOpHash, address indexed sender, address factory, address paymaster);
/**
* An event emitted if the UserOperation "callData" reverted with non-zero length
* @param userOpHash the request unique identifier.
* @param sender the sender of this request
* @param nonce the nonce used in the request
* @param revertReason - the return bytes from the (reverted) call to "callData".
*/
event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason);
/**
* an event emitted by handleOps(), before starting the execution loop.
* any event emitted before this event, is part of the validation.
*/
event BeforeExecution();
/**
* signature aggregator used by the following UserOperationEvents within this bundle.
*/
event SignatureAggregatorChanged(address indexed aggregator);
/**
* a custom revert error of handleOps, to identify the offending op.
* NOTE: if simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
* @param opIndex - index into the array of ops to the failed one (in simulateValidation, this is always zero)
* @param reason - revert reason
* The string starts with a unique code "AAmn", where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
* so a failure can be attributed to the correct entity.
* Should be caught in off-chain handleOps simulation and not happen on-chain.
* Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
*/
error FailedOp(uint256 opIndex, string reason);
/**
* error case when a signature aggregator fails to verify the aggregated signature it had created.
*/
error SignatureValidationFailed(address aggregator);
/**
* Successful result from simulateValidation.
* @param returnInfo gas and time-range returned values
* @param senderInfo stake information about the sender
* @param factoryInfo stake information about the factory (if any)
* @param paymasterInfo stake information about the paymaster (if any)
*/
error ValidationResult(ReturnInfo returnInfo,
StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo);
/**
* Successful result from simulateValidation, if the account returns a signature aggregator
* @param returnInfo gas and time-range returned values
* @param senderInfo stake information about the sender
* @param factoryInfo stake information about the factory (if any)
* @param paymasterInfo stake information about the paymaster (if any)
* @param aggregatorInfo signature aggregation info (if the account requires signature aggregator)
* bundler MUST use it to verify the signature, or reject the UserOperation
*/
error ValidationResultWithAggregation(ReturnInfo returnInfo,
StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo,
AggregatorStakeInfo aggregatorInfo);
/**
* return value of getSenderAddress
*/
error SenderAddressResult(address sender);
/**
* return value of simulateHandleOp
*/
error ExecutionResult(uint256 preOpGas, uint256 paid, uint48 validAfter, uint48 validUntil, bool targetSuccess, bytes targetResult);
//UserOps handled, per aggregator
struct UserOpsPerAggregator {
UserOperation[] userOps;
// aggregator address
IAggregator aggregator;
// aggregated signature
bytes signature;
}
/**
* Execute a batch of UserOperation.
* no signature aggregator is used.
* if any account requires an aggregator (that is, it returned an aggregator when
* performing simulateValidation), then handleAggregatedOps() must be used instead.
* @param ops the operations to execute
* @param beneficiary the address to receive the fees
*/
function handleOps(UserOperation[] calldata ops, address payable beneficiary) external;
/**
* Execute a batch of UserOperation with Aggregators
* @param opsPerAggregator the operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts)
* @param beneficiary the address to receive the fees
*/
function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
address payable beneficiary
) external;
/**
* generate a request Id - unique identifier for this request.
* the request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
*/
function getUserOpHash(UserOperation calldata userOp) external view returns (bytes32);
/**
* Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.
* @dev this method always revert. Successful result is ValidationResult error. other errors are failures.
* @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data.
* @param userOp the user operation to validate.
*/
function simulateValidation(UserOperation calldata userOp) external;
/**
* gas and return values during simulation
* @param preOpGas the gas used for validation (including preValidationGas)
* @param prefund the required prefund for this operation
* @param sigFailed validateUserOp's (or paymaster's) signature check failed
* @param validAfter - first timestamp this UserOp is valid (merging account and paymaster time-range)
* @param validUntil - last timestamp this UserOp is valid (merging account and paymaster time-range)
* @param paymasterContext returned by validatePaymasterUserOp (to be passed into postOp)
*/
struct ReturnInfo {
uint256 preOpGas;
uint256 prefund;
bool sigFailed;
uint48 validAfter;
uint48 validUntil;
bytes paymasterContext;
}
/**
* returned aggregated signature info.
* the aggregator returned by the account, and its current stake.
*/
struct AggregatorStakeInfo {
address aggregator;
StakeInfo stakeInfo;
}
/**
* Get counterfactual sender address.
* Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
* this method always revert, and returns the address in SenderAddressResult error
* @param initCode the constructor code to be passed into the UserOperation.
*/
function getSenderAddress(bytes memory initCode) external;
/**
* simulate full execution of a UserOperation (including both validation and target execution)
* this method will always revert with "ExecutionResult".
* it performs full validation of the UserOperation, but ignores signature error.
* an optional target address is called after the userop succeeds, and its value is returned
* (before the entire call is reverted)
* Note that in order to collect the the success/failure of the target call, it must be executed
* with trace enabled to track the emitted events.
* @param op the UserOperation to simulate
* @param target if nonzero, a target address to call after userop simulation. If called, the targetSuccess and targetResult
* are set to the return from that call.
* @param targetCallData callData to pass to target address
*/
function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
abstract contract Compatibility {
receive() external payable {}
function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
return this.onERC721Received.selector;
}
function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata)
external
pure
returns (bytes4)
{
return this.onERC1155BatchReceived.selector;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Importing necessary interfaces
import "account-abstraction/interfaces/IEntryPoint.sol";
import "src/interfaces/IValidator.sol";
import "src/common/Constants.sol";
import "src/common/Structs.sol";
/// @title Kernel Storage Contract
/// @author taek<leekt216@gmail.com>
/// @notice This contract serves as the storage module for the Kernel contract.
/// @dev This contract should only be used by the main Kernel contract.
contract KernelStorage {
IEntryPoint public immutable entryPoint; // The entry point of the contract
// Event declarations
event Upgraded(address indexed newImplementation);
event DefaultValidatorChanged(address indexed oldValidator, address indexed newValidator);
event ExecutionChanged(bytes4 indexed selector, address indexed executor, address indexed validator);
// Error declarations
error NotAuthorizedCaller();
error AlreadyInitialized();
// Modifier to check if the function is called by the entry point, the contract itself or the owner
modifier onlyFromEntryPointOrSelf() {
if (msg.sender != address(entryPoint) && msg.sender != address(this)) {
revert NotAuthorizedCaller();
}
_;
}
/// @param _entryPoint The address of the EntryPoint contract
/// @dev Sets up the EntryPoint contract address
constructor(IEntryPoint _entryPoint) {
entryPoint = _entryPoint;
getKernelStorage().defaultValidator = IKernelValidator(address(1));
}
// Function to initialize the wallet kernel
function initialize(IKernelValidator _defaultValidator, bytes calldata _data) external payable {
_setInitialData(_defaultValidator, _data);
}
// Function to get the wallet kernel storage
function getKernelStorage() internal pure returns (WalletKernelStorage storage ws) {
assembly {
ws.slot := KERNEL_STORAGE_SLOT
}
}
// Function to upgrade the contract to a new implementation
function upgradeTo(address _newImplementation) external payable onlyFromEntryPointOrSelf {
assembly {
sstore(IMPLEMENTATION_SLOT, _newImplementation)
}
emit Upgraded(_newImplementation);
}
// Functions to get the nonce from the entry point
function getNonce() public view virtual returns (uint256) {
return entryPoint.getNonce(address(this), 0);
}
function getNonce(uint192 key) public view virtual returns (uint256) {
return entryPoint.getNonce(address(this), key);
}
// query storage
function getDefaultValidator() public view returns (IKernelValidator validator) {
assembly {
validator := shr(80, sload(KERNEL_STORAGE_SLOT_1))
}
}
function getDisabledMode() public view returns (bytes4 disabled) {
assembly {
disabled := shl(224, sload(KERNEL_STORAGE_SLOT_1))
}
}
function getLastDisabledTime() public view returns (uint48) {
return getKernelStorage().lastDisabledTime;
}
/// @notice Returns the execution details for a specific function signature
/// @dev This function can be used to get execution details for a specific function signature
/// @param _selector The function signature
/// @return ExecutionDetail struct containing the execution details
function getExecution(bytes4 _selector) public view returns (ExecutionDetail memory) {
return getKernelStorage().execution[_selector];
}
/// @notice Changes the execution details for a specific function selector
/// @dev This function can only be called from the EntryPoint contract, the contract owner, or itself
/// @param _selector The selector of the function for which execution details are being set
/// @param _executor The executor to be associated with the function selector
/// @param _validator The validator contract that will be responsible for validating operations associated with this function selector
/// @param _validUntil The timestamp until which the execution details are valid
/// @param _validAfter The timestamp after which the execution details are valid
function setExecution(
bytes4 _selector,
address _executor,
IKernelValidator _validator,
uint48 _validUntil,
uint48 _validAfter,
bytes calldata _enableData
) external payable onlyFromEntryPointOrSelf {
getKernelStorage().execution[_selector] = ExecutionDetail({
executor: _executor,
validator: _validator,
validUntil: ValidUntil.wrap(_validUntil),
validAfter: ValidAfter.wrap(_validAfter)
});
_validator.enable(_enableData);
emit ExecutionChanged(_selector, _executor, address(_validator));
}
function setDefaultValidator(IKernelValidator _defaultValidator, bytes calldata _data)
external
payable
onlyFromEntryPointOrSelf
{
IKernelValidator oldValidator = getKernelStorage().defaultValidator;
getKernelStorage().defaultValidator = _defaultValidator;
emit DefaultValidatorChanged(address(oldValidator), address(_defaultValidator));
_defaultValidator.enable(_data);
}
/// @notice Updates the disabled mode
/// @dev This function can be used to update the disabled mode
/// @param _disableFlag The new disabled mode
function disableMode(bytes4 _disableFlag) external payable onlyFromEntryPointOrSelf {
getKernelStorage().disabledMode = _disableFlag;
getKernelStorage().lastDisabledTime = uint48(block.timestamp);
}
function _setInitialData(IKernelValidator _defaultValidator, bytes calldata _data) internal virtual {
address validator;
assembly {
validator := shr(80, sload(KERNEL_STORAGE_SLOT_1))
}
if (address(validator) != address(0)) {
revert AlreadyInitialized();
}
getKernelStorage().defaultValidator = _defaultValidator;
_defaultValidator.enable(_data);
}
}// 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 }
}
}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;
pragma solidity ^0.8.0;
enum Operation {
Call,
DelegateCall
}
enum ParamCondition {
EQUAL,
GREATER_THAN,
LESS_THAN,
GREATER_THAN_OR_EQUAL,
LESS_THAN_OR_EQUAL,
NOT_EQUAL
}// 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;
}
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.12;
/**
* manage deposits and stakes.
* deposit is just a balance used to pay for UserOperations (either by a paymaster or an account)
* stake is value locked for at least "unstakeDelay" by the staked entity.
*/
interface IStakeManager {
event Deposited(
address indexed account,
uint256 totalDeposit
);
event Withdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
/// Emitted when stake or unstake delay are modified
event StakeLocked(
address indexed account,
uint256 totalStaked,
uint256 unstakeDelaySec
);
/// Emitted once a stake is scheduled for withdrawal
event StakeUnlocked(
address indexed account,
uint256 withdrawTime
);
event StakeWithdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
/**
* @param deposit the entity's deposit
* @param staked true if this entity is staked.
* @param stake actual amount of ether staked for this entity.
* @param unstakeDelaySec minimum delay to withdraw the stake.
* @param withdrawTime - first block timestamp where 'withdrawStake' will be callable, or zero if already locked
* @dev sizes were chosen so that (deposit,staked, stake) fit into one cell (used during handleOps)
* and the rest fit into a 2nd cell.
* 112 bit allows for 10^15 eth
* 48 bit for full timestamp
* 32 bit allows 150 years for unstake delay
*/
struct DepositInfo {
uint112 deposit;
bool staked;
uint112 stake;
uint32 unstakeDelaySec;
uint48 withdrawTime;
}
//API struct used by getStakeInfo and simulateValidation
struct StakeInfo {
uint256 stake;
uint256 unstakeDelaySec;
}
/// @return info - full deposit information of given account
function getDepositInfo(address account) external view returns (DepositInfo memory info);
/// @return the deposit (for gas payment) of the account
function balanceOf(address account) external view returns (uint256);
/**
* add to the deposit of the given account
*/
function depositTo(address account) external payable;
/**
* add to the account's stake - amount and delay
* any pending unstake is first cancelled.
* @param _unstakeDelaySec the new lock duration before the deposit can be withdrawn.
*/
function addStake(uint32 _unstakeDelaySec) external payable;
/**
* attempt to unlock the stake.
* the value can be withdrawn (using withdrawStake) after the unstake delay.
*/
function unlockStake() external;
/**
* withdraw from the (unlocked) stake.
* must first call unlockStake and wait for the unstakeDelay to pass
* @param withdrawAddress the address to send withdrawn value.
*/
function withdrawStake(address payable withdrawAddress) external;
/**
* withdraw from the deposit.
* @param withdrawAddress the address to send withdrawn value.
* @param withdrawAmount the amount to withdraw.
*/
function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
import "./UserOperation.sol";
/**
* Aggregated Signatures validator.
*/
interface IAggregator {
/**
* validate aggregated signature.
* revert if the aggregated signature does not match the given list of operations.
*/
function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) external view;
/**
* validate signature of a single userOp
* This method is should be called by bundler after EntryPoint.simulateValidation() returns (reverts) with ValidationResultWithAggregation
* First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
* @param userOp the userOperation received from the user.
* @return sigForUserOp the value to put into the signature field of the userOp when calling handleOps.
* (usually empty, unless account and aggregator support some kind of "multisig"
*/
function validateUserOpSignature(UserOperation calldata userOp)
external view returns (bytes memory sigForUserOp);
/**
* aggregate multiple signatures into a single value.
* This method is called off-chain to calculate the signature to pass with handleOps()
* bundler MAY use optimized custom code perform this aggregation
* @param userOps array of UserOperations to collect the signatures from.
* @return aggregatedSignature the aggregated signature
*/
function aggregateSignatures(UserOperation[] calldata userOps) external view returns (bytes memory aggregatedSignature);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
interface INonceManager {
/**
* Return the next nonce for this sender.
* Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop)
* But UserOp with different keys can come with arbitrary order.
*
* @param sender the account address
* @param key the high 192 bit of the nonce
* @return nonce a full nonce to pass for next UserOp with this sender.
*/
function getNonce(address sender, uint192 key)
external view returns (uint256 nonce);
/**
* Manually increment the nonce of the sender.
* This method is exposed just for completeness..
* Account does NOT need to call it, neither during validation, nor elsewhere,
* as the EntryPoint will update the nonce regardless.
* Possible use-case is call it with various keys to "initialize" their nonces to one, so that future
* UserOperations will not pay extra for the first transaction with a given key.
*/
function incrementNonce(uint192 key) external;
}// 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 userOppragma solidity ^0.8.0;
import "src/interfaces/IValidator.sol";
import "src/common/Enum.sol";
import "src/common/Types.sol";
// Defining a struct for execution details
struct ExecutionDetail {
ValidAfter validAfter; // Until what time is this execution valid
ValidUntil validUntil; // After what time is this execution valid
address executor; // Who is the executor of this execution
IKernelValidator validator; // The validator for this execution
}
// Defining a struct for wallet kernel storage
struct WalletKernelStorage {
bytes32 __deprecated; // A deprecated field
bytes4 disabledMode; // Mode which is currently disabled
uint48 lastDisabledTime; // Last time when a mode was disabled
IKernelValidator defaultValidator; // Default validator for the wallet
mapping(bytes4 => ExecutionDetail) execution; // Mapping of function selectors to execution details
}
// Param Rule for session key
struct ParamRule {
uint256 offset;
ParamCondition condition;
bytes32 param;
}
struct Permission {
address target;
uint256 valueLimit;
bytes4 sig;
ParamRule[] rules;
Operation operation;
}
struct SessionData {
bytes32 merkleRoot;
ValidAfter validAfter;
ValidUntil validUntil;
address paymaster; // address(0) means accept userOp without paymaster, address(1) means reject userOp with paymaster, other address means accept userOp with paymaster with the address
bool enabled;
}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)
}
}// 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)
}
}{
"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 ABI
API[{"inputs":[{"internalType":"contract IEntryPoint","name":"_entryPoint","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"DisabledMode","type":"error"},{"inputs":[],"name":"NotAuthorizedCaller","type":"error"},{"inputs":[],"name":"NotEntryPoint","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldValidator","type":"address"},{"indexed":true,"internalType":"address","name":"newValidator","type":"address"}],"name":"DefaultValidatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"selector","type":"bytes4"},{"indexed":true,"internalType":"address","name":"executor","type":"address"},{"indexed":true,"internalType":"address","name":"validator","type":"address"}],"name":"ExecutionChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"bytes4","name":"_disableFlag","type":"bytes4"}],"name":"disableMode","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"contract IEntryPoint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum Operation","name":"operation","type":"uint8"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getDefaultValidator","outputs":[{"internalType":"contract IKernelValidator","name":"validator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDisabledMode","outputs":[{"internalType":"bytes4","name":"disabled","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"getExecution","outputs":[{"components":[{"internalType":"ValidAfter","name":"validAfter","type":"uint48"},{"internalType":"ValidUntil","name":"validUntil","type":"uint48"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"contract IKernelValidator","name":"validator","type":"address"}],"internalType":"struct ExecutionDetail","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastDisabledTime","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint192","name":"key","type":"uint192"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IKernelValidator","name":"_defaultValidator","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"contract IKernelValidator","name":"_defaultValidator","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"setDefaultValidator","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"},{"internalType":"address","name":"_executor","type":"address"},{"internalType":"contract IKernelValidator","name":"_validator","type":"address"},{"internalType":"uint48","name":"_validUntil","type":"uint48"},{"internalType":"uint48","name":"_validAfter","type":"uint48"},{"internalType":"bytes","name":"_enableData","type":"bytes"}],"name":"setExecution","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"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":"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":"missingAccountFunds","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"ValidationData","name":"validationData","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.