Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 25 from a total of 40 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Execute | 24460402 | 2 hrs ago | IN | 0.00021191 ETH | 0.00001163 | ||||
| Execute | 24443489 | 2 days ago | IN | 0.00006839 ETH | 0.00001906 | ||||
| Execute | 24442277 | 2 days ago | IN | 0.00006839 ETH | 0.00005079 | ||||
| Execute | 24440691 | 2 days ago | IN | 0.00006839 ETH | 0.00133231 | ||||
| Execute | 24434690 | 3 days ago | IN | 0.00021191 ETH | 0.00011567 | ||||
| Execute | 24434668 | 3 days ago | IN | 0.00021191 ETH | 0.0001369 | ||||
| Execute | 24434608 | 3 days ago | IN | 0.00021191 ETH | 0.00012742 | ||||
| Execute | 24434558 | 3 days ago | IN | 0.00021191 ETH | 0.00013338 | ||||
| Execute | 24434526 | 3 days ago | IN | 0.00021191 ETH | 0.00012823 | ||||
| Execute | 24434390 | 3 days ago | IN | 0.00021191 ETH | 0.00023828 | ||||
| Execute | 24434076 | 3 days ago | IN | 0.00021191 ETH | 0.00018581 | ||||
| Execute | 24434074 | 3 days ago | IN | 0.00021191 ETH | 0.00017527 | ||||
| Execute | 24434060 | 3 days ago | IN | 0.00021191 ETH | 0.00017982 | ||||
| Execute | 24434055 | 3 days ago | IN | 0.00021191 ETH | 0.00018609 | ||||
| Execute | 24433965 | 3 days ago | IN | 0.00021191 ETH | 0.00005144 | ||||
| Execute | 24433906 | 3 days ago | IN | 0.00021191 ETH | 0.00004184 | ||||
| Execute | 24433730 | 3 days ago | IN | 0.00021191 ETH | 0.00003745 | ||||
| Execute | 24433446 | 3 days ago | IN | 0.00021191 ETH | 0.00004164 | ||||
| Execute | 24433333 | 3 days ago | IN | 0.00021191 ETH | 0.00003235 | ||||
| Execute | 24433237 | 3 days ago | IN | 0.00006839 ETH | 0.0000341 | ||||
| Execute | 24433193 | 3 days ago | IN | 0.00021191 ETH | 0.00003249 | ||||
| Execute | 24432404 | 4 days ago | IN | 0.00021191 ETH | 0.00002573 | ||||
| Execute | 24432400 | 4 days ago | IN | 0.00021191 ETH | 0.0000279 | ||||
| Execute | 24432113 | 4 days ago | IN | 0.00021191 ETH | 0.00001941 | ||||
| Execute | 24432002 | 4 days ago | IN | 0.00021191 ETH | 0.00002465 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Execute From Exe... | 24460402 | 2 hrs ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24443489 | 2 days ago | 0.00006839 ETH | ||||
| Execute From Exe... | 24442277 | 2 days ago | 0.00006839 ETH | ||||
| Execute From Exe... | 24440691 | 2 days ago | 0.00006839 ETH | ||||
| Execute From Exe... | 24434690 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24434668 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24434608 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24434558 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24434526 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24434390 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24434076 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24434074 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24434060 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24434055 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24433965 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24433906 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24433730 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24433446 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24433333 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24433237 | 3 days ago | 0.00006839 ETH | ||||
| Execute From Exe... | 24433193 | 3 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24432404 | 4 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24432400 | 4 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24432113 | 4 days ago | 0.00021191 ETH | ||||
| Execute From Exe... | 24432002 | 4 days ago | 0.00021191 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ECDSAExecutor
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai 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 {EIP712} from "solady/utils/EIP712.sol";
import {ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol";
import {IExecutor} from "../interfaces/IERC7579Modules.sol";
import {IERC7579Account, ExecMode} from "../interfaces/IERC7579Account.sol";
import {MODULE_TYPE_EXECUTOR} from "../types/Constants.sol";
struct ECDSAExecutorStorage {
address owner;
mapping(uint192 => uint64) nonceSequenceNumber;
}
/**
* @notice ECDSAExecutor enables external execution of transactions via ECDSA signatures
* @dev This executor uses EIP-712 typed data signing which includes chainId in the domain separator.
* Signatures are chain-specific and cannot be replayed across different chains.
* If deploying on multiple chains, ensure each deployment uses unique nonces or different owners.
*
* Security features:
* - EIP-712 domain separation (includes chainId)
* - 2D nonce system for parallel execution
* - Time-bound signatures with maximum 30-day validity
* - Signature malleability protection
* - Reentrancy guard protection
*/
contract ECDSAExecutor is IExecutor, EIP712, ReentrancyGuard {
error InvalidNonce(address account, uint256 expected, uint256 actual);
error SignatureExpired(address account, uint256 expiration);
error InvalidSignature();
error InvalidOwner();
error InvalidAccount();
error MalleableSignature();
error ExpirationTooFar(uint256 expiration);
bytes32 constant EXECUTE_TYPEHASH = keccak256(
"Execute(address account,uint256 mode,bytes executionCalldata,uint256 nonce,uint256 expiration)"
);
uint256 constant MAX_EXPIRATION_DURATION = 30 days;
event OwnerRegistered(address indexed kernel, address indexed owner);
event OwnerUnregistered(address indexed kernel, address indexed owner);
event OwnerTransferred(address indexed account, address indexed oldOwner, address indexed newOwner);
event ExecutionRequested(address indexed kernel, bytes32 indexed executionHash);
event NonceIncremented(address indexed account, uint192 indexed key, uint64 newSequence);
/// @dev Storage for each account's executor configuration
/// Nonce keys should be derived using deriveNonceKey() to avoid collisions
/// Common patterns:
/// - Key 0: Default sequential execution
/// - Key 1-999: Reserved for protocol use
/// - Key 1000+: Application-specific channels
mapping(address => ECDSAExecutorStorage) internal ecdsaExecutorStorage;
function onInstall(bytes calldata _data) external payable override {
if (_isInitialized(msg.sender)) revert AlreadyInitialized(msg.sender);
address owner = abi.decode(_data, (address));
if (owner == address(0)) revert InvalidOwner();
ecdsaExecutorStorage[msg.sender].owner = owner;
emit OwnerRegistered(msg.sender, owner);
}
function onUninstall(bytes calldata) external payable override {
if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
address owner = ecdsaExecutorStorage[msg.sender].owner;
delete ecdsaExecutorStorage[msg.sender];
emit OwnerUnregistered(msg.sender, owner);
}
function isModuleType(uint256 typeID) external pure override returns (bool) {
return typeID == MODULE_TYPE_EXECUTOR;
}
function isInitialized(address smartAccount) external view override returns (bool) {
return _isInitialized(smartAccount);
}
function _isInitialized(address smartAccount) internal view returns (bool) {
return ecdsaExecutorStorage[smartAccount].owner != address(0);
}
/// @notice Validates that a signature is not malleable (s <= N/2)
/// @param signature The signature bytes to validate
function _validateSignatureNotMalleable(bytes calldata signature) internal pure {
// Extract 's' value from signature (bytes 32-63)
bytes32 s;
assembly {
s := calldataload(add(signature.offset, 0x20))
}
// Check if s > N/2 (N is the secp256k1 curve order)
// N/2 + 1 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A1
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert MalleableSignature();
}
}
function getOwner(address account) external view returns (address) {
return ecdsaExecutorStorage[account].owner;
}
/// @notice Transfers ownership to a new address
/// @dev Can only be called by the account itself (not the owner)
/// @param newOwner The address of the new owner
function transferOwnership(address newOwner) external {
if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
if (newOwner == address(0)) revert InvalidOwner();
address oldOwner = ecdsaExecutorStorage[msg.sender].owner;
ecdsaExecutorStorage[msg.sender].owner = newOwner;
emit OwnerTransferred(msg.sender, oldOwner, newOwner);
}
function getNonce(address account, uint192 key) external view returns (uint64) {
return ecdsaExecutorStorage[account].nonceSequenceNumber[key];
}
function _packNonce(uint192 key, uint256 sequence) internal pure returns (uint256) {
return (uint256(key) << 64) | sequence;
}
function _unpackNonce(uint256 nonce) internal pure returns (uint192 key, uint64 sequence) {
key = uint192(nonce >> 64);
sequence = uint64(nonce);
}
function incrementNonce(uint192 key) external {
// Only the account itself can increment its nonce
if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
ecdsaExecutorStorage[msg.sender].nonceSequenceNumber[key]++;
emit NonceIncremented(msg.sender, key, ecdsaExecutorStorage[msg.sender].nonceSequenceNumber[key]);
}
/// @notice Derives a deterministic nonce key from a purpose identifier
/// @dev Helps prevent nonce key collisions by using a hash-based derivation
/// @param purposeSalt A unique identifier for the nonce channel purpose
/// @return The derived nonce key
function deriveNonceKey(bytes32 purposeSalt) external pure returns (uint192) {
return uint192(uint256(keccak256(abi.encodePacked("ECDSAExecutor", purposeSalt))) >> 64);
}
function execute(
address account,
ExecMode mode,
bytes calldata executionCalldata,
uint256 nonce,
uint256 expiration,
bytes calldata signature
) external payable nonReentrant returns (bytes[] memory returnData) {
// Validate account is not zero address
if (account == address(0)) revert InvalidAccount();
if (!_isInitialized(account)) revert NotInitialized(account);
// Validate expiration is within allowed range
if (expiration > block.timestamp + MAX_EXPIRATION_DURATION) {
revert ExpirationTooFar(expiration);
}
if (block.timestamp > expiration) {
revert SignatureExpired(account, expiration);
}
// Validate and update nonce
_validateAndUpdateNonce(account, nonce);
// Validate signature is not malleable
_validateSignatureNotMalleable(signature);
// Build EIP-712 struct hash
bytes32 structHash = keccak256(
abi.encode(
EXECUTE_TYPEHASH,
account,
ExecMode.unwrap(mode),
keccak256(executionCalldata),
nonce,
expiration
)
);
bytes32 digest = _hashTypedData(structHash);
emit ExecutionRequested(account, digest);
// Verify signature using EIP-712 digest
address owner = ecdsaExecutorStorage[account].owner;
if (owner != ECDSA.recover(digest, signature)) {
revert InvalidSignature();
}
return IERC7579Account(account).executeFromExecutor{value: msg.value}(mode, executionCalldata);
}
function _validateAndUpdateNonce(address account, uint256 nonce) internal {
(uint192 key, uint256 sequence) = _unpackNonce(nonce);
uint64 expectedSequence = ecdsaExecutorStorage[account].nonceSequenceNumber[key];
if (sequence != expectedSequence) {
revert InvalidNonce(account, _packNonce(key, expectedSequence), nonce);
}
ecdsaExecutorStorage[account].nonceSequenceNumber[key] = uint64(sequence + 1);
emit NonceIncremented(account, key, uint64(sequence + 1));
}
function _domainNameAndVersion()
internal
pure
override
returns (string memory name, string memory version)
{
name = "ECDSAExecutor";
version = "1";
}
}// 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 directly use signatures as unique identifiers:
/// - The recovery operations do NOT check if a signature is non-malleable.
/// - 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.
/// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
library ECDSA {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The order of the secp256k1 elliptic curve.
uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;
/// @dev `N/2 + 1`. Used for checking the malleability of the signature.
uint256 private constant _HALF_N_PLUS_1 =
0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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 {
for { let m := mload(0x40) } 1 {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
} {
switch mload(signature)
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { continue }
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if returndatasize() { break }
}
}
}
/// @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 {
for { let m := mload(0x40) } 1 {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
} {
switch signature.length
case 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`.
}
case 65 {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
}
default { continue }
mstore(0x00, hash)
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if returndatasize() { break }
}
}
}
/// @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(), 1, 0x00, 0x80, 0x01, 0x20))
// `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(), 1, 0x00, 0x80, 0x01, 0x20))
// `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 {
for { let m := mload(0x40) } 1 {} {
switch mload(signature)
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
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.
break
}
}
}
/// @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 {
for { let m := mload(0x40) } 1 {} {
switch signature.length
case 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`.
}
case 65 {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
}
default { break }
mstore(0x00, hash)
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
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.
break
}
}
}
/// @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(), 1, 0x00, 0x80, 0x40, 0x20))
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(), 1, 0x00, 0x80, 0x40, 0x20))
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://ethereum.org/en/developers/docs/apis/json-rpc/#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://ethereum.org/en/developers/docs/apis/json-rpc/#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.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CANONICAL HASH FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// The following functions returns the hash of the signature in it's canonicalized format,
// which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
// If `s` is greater than `N / 2` then it will be converted to `N - s`
// and the `v` value will be flipped.
// If the signature has an invalid length, or if `v` is invalid,
// a uniquely corrupt hash will be returned.
// These functions are useful for "poor-mans-VRF".
/// @dev Returns the canonical hash of `signature`.
function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
// @solidity memory-safe-assembly
assembly {
let l := mload(signature)
for {} 1 {} {
mstore(0x00, mload(add(signature, 0x20))) // `r`.
let s := mload(add(signature, 0x40))
let v := mload(add(signature, 0x41))
if eq(l, 64) {
v := add(shr(255, s), 27)
s := shr(1, shl(1, s))
}
if iszero(lt(s, _HALF_N_PLUS_1)) {
v := xor(v, 7)
s := sub(N, s)
}
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
break
}
// If the length is neither 64 nor 65, return a uniquely corrupted hash.
if iszero(lt(sub(l, 64), 2)) {
// `bytes4(keccak256("InvalidSignatureLength"))`.
result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
}
}
}
/// @dev Returns the canonical hash of `signature`.
function canonicalHashCalldata(bytes calldata signature)
internal
pure
returns (bytes32 result)
{
// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
mstore(0x00, calldataload(signature.offset)) // `r`.
let s := calldataload(add(signature.offset, 0x20))
let v := calldataload(add(signature.offset, 0x21))
if eq(signature.length, 64) {
v := add(shr(255, s), 27)
s := shr(1, shl(1, s))
}
if iszero(lt(s, _HALF_N_PLUS_1)) {
v := xor(v, 7)
s := sub(N, s)
}
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
break
}
// If the length is neither 64 nor 65, return a uniquely corrupted hash.
if iszero(lt(sub(signature.length, 64), 2)) {
calldatacopy(mload(0x40), signature.offset, signature.length)
// `bytes4(keccak256("InvalidSignatureLength"))`.
result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2)
}
}
}
/// @dev Returns the canonical hash of `signature`.
function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
// @solidity memory-safe-assembly
assembly {
mstore(0x00, r) // `r`.
let v := add(shr(255, vs), 27)
let s := shr(1, shl(1, vs))
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the canonical hash of `signature`.
function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
// @solidity memory-safe-assembly
assembly {
mstore(0x00, r) // `r`.
if iszero(lt(s, _HALF_N_PLUS_1)) {
v := xor(v, 7)
s := sub(N, s)
}
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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 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)
///
/// @dev 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;
uint256 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 = uint256(uint160(address(this)));
_cachedChainId = block.chainid;
string memory name;
string memory version;
if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
bytes32 versionHash =
_domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
_cachedNameHash = nameHash;
_cachedVersionHash = versionHash;
bytes32 separator;
if (!_domainNameAndVersionMayChange()) {
/// @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";
/// }
/// ```
///
/// Note: If the returned result may change after the contract has been deployed,
/// you must override `_domainNameAndVersionMayChange()` to return true.
function _domainNameAndVersion()
internal
view
virtual
returns (string memory name, string memory version);
/// @dev Returns if `_domainNameAndVersion()` may change
/// after the contract has been deployed (i.e. after the constructor).
/// Default: false.
function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _domainSeparator() internal view virtual returns (bytes32 separator) {
if (_domainNameAndVersionMayChange()) {
separator = _buildDomainSeparator();
} else {
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) {
// We will use `digest` to store the domain separator to save a bit of gas.
if (_domainNameAndVersionMayChange()) {
digest = _buildDomainSeparator();
} else {
digest = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
}
/// @solidity memory-safe-assembly
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, digest) // 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) {
// We will use `separator` to store the name hash to save a bit of gas.
bytes32 versionHash;
if (_domainNameAndVersionMayChange()) {
(string memory name, string memory version) = _domainNameAndVersion();
separator = keccak256(bytes(name));
versionHash = keccak256(bytes(version));
} else {
separator = _cachedNameHash;
versionHash = _cachedVersionHash;
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
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;
uint256 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 Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unauthorized reentrant call.
error Reentrancy();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
/// 9 bytes is large enough to avoid collisions with lower slots,
/// but not too large to result in excessive bytecode bloat.
uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* REENTRANCY GUARD */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Guards a function from reentrancy.
modifier nonReentrant() virtual {
/// @solidity memory-safe-assembly
assembly {
if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
mstore(0x00, 0xab143c06) // `Reentrancy()`.
revert(0x1c, 0x04)
}
sstore(_REENTRANCY_GUARD_SLOT, address())
}
_;
/// @solidity memory-safe-assembly
assembly {
sstore(_REENTRANCY_GUARD_SLOT, codesize())
}
}
/// @dev Guards a view function from read-only reentrancy.
modifier nonReadReentrant() virtual {
/// @solidity memory-safe-assembly
assembly {
if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
mstore(0x00, 0xab143c06) // `Reentrancy()`.
revert(0x1c, 0x04)
}
}
_;
}
}// 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 validation 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 {
function preCheck(address msgSender, uint256 msgValue, bytes calldata msgData)
external
payable
returns (bytes memory hookData);
function postCheck(bytes calldata hookData) 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: MIT
pragma solidity ^0.8.21;
import {CallType, ExecType, ExecMode} from "../utils/ExecLib.sol";
import {PackedUserOperation} from "./PackedUserOperation.sol";
struct Execution {
address target;
uint256 value;
bytes callData;
}
interface IERC7579Account {
event ModuleInstalled(uint256 moduleTypeId, address module);
event ModuleUninstalled(uint256 moduleTypeId, address module);
/**
* @dev Executes a transaction on behalf of the account.
* This function is intended to be called by ERC-4337 EntryPoint.sol
* @dev Ensure adequate authorization control: i.e. onlyEntryPointOrSelf
*
* @dev MSA MUST implement this function signature.
* If a mode is requested that is not supported by the Account, it MUST revert
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*/
function execute(ExecMode mode, bytes calldata executionCalldata) external payable;
/**
* @dev Executes a transaction on behalf of the account.
* This function is intended to be called by Executor Modules
* @dev Ensure adequate authorization control: i.e. onlyExecutorModule
*
* @dev MSA MUST implement this function signature.
* If a mode is requested that is not supported by the Account, it MUST revert
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*/
function executeFromExecutor(ExecMode mode, bytes calldata executionCalldata)
external
payable
returns (bytes[] memory returnData);
/**
* @dev ERC-1271 isValidSignature
* This function is intended to be used to validate a smart account signature
* and may forward the call to a validator module
*
* @param hash The hash of the data that is signed
* @param data The data that is signed
*/
function isValidSignature(bytes32 hash, bytes calldata data) external view returns (bytes4);
/**
* @dev installs a Module of a certain type on the smart account
* @dev Implement Authorization control of your choosing
* @param moduleTypeId the module type ID according the ERC-7579 spec
* @param module the module address
* @param initData arbitrary data that may be required on the module during `onInstall`
* initialization.
*/
function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external payable;
/**
* @dev uninstalls a Module of a certain type on the smart account
* @dev Implement Authorization control of your choosing
* @param moduleTypeId the module type ID according the ERC-7579 spec
* @param module the module address
* @param deInitData arbitrary data that may be required on the module during `onUninstall`
* de-initialization.
*/
function uninstallModule(uint256 moduleTypeId, address module, bytes calldata deInitData) external payable;
/**
* Function to check if the account supports a certain CallType or ExecType (see ModeLib.sol)
* @param encodedMode the encoded mode
*/
function supportsExecutionMode(ExecMode encodedMode) external view returns (bool);
/**
* Function to check if the account supports installation of a certain module type Id
* @param moduleTypeId the module type ID according the ERC-7579 spec
*/
function supportsModule(uint256 moduleTypeId) external view returns (bool);
/**
* Function to check if the account has a certain module installed
* @param moduleTypeId the module type ID according the ERC-7579 spec
* Note: keep in mind that some contracts can be multiple module types at the same time. It
* thus may be necessary to query multiple module types
* @param module the module address
* @param additionalContext additional context data that the smart account may interpret to
* identify conditions under which the module is installed.
* usually this is not necessary, but for some special hooks that
* are stored in mappings, this param might be needed
*/
function isModuleInstalled(uint256 moduleTypeId, address module, bytes calldata additionalContext)
external
view
returns (bool);
/**
* @dev Returns the account id of the smart account
* @return accountImplementationId the account id of the smart account
* the accountId should be structured like so:
* "vendorname.accountname.semver"
*/
function accountId() external view returns (string memory accountImplementationId);
}// 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_7702 = ValidationType.wrap(0x00);
ValidationType constant VALIDATION_TYPE_VALIDATOR = ValidationType.wrap(0x01);
ValidationType constant VALIDATION_TYPE_PERMISSION = ValidationType.wrap(0x02);
// --- Kernel Hook constants ---
address constant HOOK_MODULE_NOT_INSTALLED = address(0);
address constant HOOK_MODULE_INSTALLED = address(1);
address constant HOOK_ONLY_ENTRYPOINT = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);
// --- EIP7702 constants ---
bytes3 constant EIP7702_PREFIX = bytes3(0xef0100);
// --- 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;
bytes32 constant MAGIC_VALUE_SIG_REPLAYABLE = keccak256("kernel.replayable.signature");
// --- 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: 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.23;
import {ExecMode, CallType, ExecType, ExecModeSelector, ExecModePayload} from "../types/Types.sol";
import {LibERC7579} from "solady/accounts/LibERC7579.sol";
import {
CALLTYPE_SINGLE,
CALLTYPE_BATCH,
EXECTYPE_DEFAULT,
EXEC_MODE_DEFAULT,
EXECTYPE_TRY,
CALLTYPE_DELEGATECALL
} from "../types/Constants.sol";
import {Execution} from "../types/Structs.sol";
/**
* @dev ExecLib is a helper library for execution
*/
library ExecLib {
error ExecutionFailed();
event TryExecuteUnsuccessful(uint256 batchExecutionindex, bytes result);
function execute(ExecMode execMode, bytes calldata executionCalldata)
internal
returns (bytes[] memory returnData)
{
(CallType callType, ExecType execType,,) = decode(execMode);
// check if calltype is batch or single
if (callType == CALLTYPE_BATCH) {
// destructure executionCallData according to batched exec
bytes32[] calldata pointers = LibERC7579.decodeBatch(executionCalldata);
// check if execType is revert or try
if (execType == EXECTYPE_DEFAULT) returnData = execute(pointers);
else if (execType == EXECTYPE_TRY) returnData = tryExecute(pointers);
else revert("Unsupported");
} else if (callType == CALLTYPE_SINGLE) {
// destructure executionCallData according to single exec
(address target, uint256 value, bytes calldata callData) = LibERC7579.decodeSingle(executionCalldata);
returnData = new bytes[](1);
bool success;
// check if execType is revert or try
if (execType == EXECTYPE_DEFAULT) {
returnData[0] = execute(target, value, callData);
} else if (execType == EXECTYPE_TRY) {
(success, returnData[0]) = tryExecute(target, value, callData);
if (!success) emit TryExecuteUnsuccessful(0, returnData[0]);
} else {
revert("Unsupported");
}
} else if (callType == CALLTYPE_DELEGATECALL) {
returnData = new bytes[](1);
(address delegate, bytes calldata callData) = LibERC7579.decodeDelegate(executionCalldata);
bool success;
(success, returnData[0]) = executeDelegatecall(delegate, callData);
if (execType == EXECTYPE_TRY) {
if (!success) emit TryExecuteUnsuccessful(0, returnData[0]);
} else if (execType == EXECTYPE_DEFAULT) {
if (!success) revert("Delegatecall failed");
} else {
revert("Unsupported");
}
} else {
revert("Unsupported");
}
}
function execute(bytes32[] calldata pointers) internal returns (bytes[] memory result) {
uint256 length = pointers.length;
result = new bytes[](length);
unchecked {
for (uint256 i; i < length; i++) {
(address target, uint256 value, bytes calldata data) = LibERC7579.getExecution(pointers, i);
result[i] = execute(target, value, data);
}
}
}
function tryExecute(bytes32[] calldata pointers) internal returns (bytes[] memory result) {
uint256 length = pointers.length;
result = new bytes[](length);
unchecked {
for (uint256 i; i < length; i++) {
(address target, uint256 value, bytes calldata data) = LibERC7579.getExecution(pointers, i);
bool success;
(success, result[i]) = tryExecute(target, value, data);
if (!success) emit TryExecuteUnsuccessful(i, result[i]);
}
}
}
function execute(address target, uint256 value, bytes calldata callData) internal returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
calldatacopy(result, callData.offset, callData.length)
if iszero(call(gas(), target, value, result, callData.length, codesize(), 0x00)) {
// Bubble up the revert if the call reverts.
returndatacopy(result, 0x00, returndatasize())
revert(result, returndatasize())
}
mstore(result, returndatasize()) // Store the length.
let o := add(result, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}
}
function tryExecute(address target, uint256 value, bytes calldata callData)
internal
returns (bool success, bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
calldatacopy(result, callData.offset, callData.length)
success := call(gas(), target, value, result, callData.length, codesize(), 0x00)
mstore(result, returndatasize()) // Store the length.
let o := add(result, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}
}
/// @dev Execute a delegatecall with `delegate` on this account.
function executeDelegatecall(address delegate, bytes calldata callData)
internal
returns (bool success, bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
calldatacopy(result, callData.offset, callData.length)
// Forwards the `data` to `delegate` via delegatecall.
success := delegatecall(gas(), delegate, result, callData.length, codesize(), 0x00)
mstore(result, returndatasize()) // Store the length.
let o := add(result, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}
}
function decode(ExecMode mode)
internal
pure
returns (CallType _calltype, ExecType _execType, ExecModeSelector _modeSelector, ExecModePayload _modePayload)
{
assembly {
_calltype := mode
_execType := shl(8, mode)
_modeSelector := shl(48, mode)
_modePayload := shl(80, mode)
}
}
function encode(CallType callType, ExecType execType, ExecModeSelector mode, ExecModePayload payload)
internal
pure
returns (ExecMode)
{
return ExecMode.wrap(
bytes32(abi.encodePacked(callType, execType, bytes4(0), ExecModeSelector.unwrap(mode), payload))
);
}
function getCallType(ExecMode mode) internal pure returns (CallType calltype) {
assembly {
calltype := mode
}
}
function encodeBatch(Execution[] memory executions) internal pure returns (bytes memory callData) {
callData = abi.encode(executions);
}
function decodeSingle(bytes calldata executionCalldata)
internal
pure
returns (address target, uint256 value, bytes calldata callData)
{
target = address(bytes20(executionCalldata[0:20]));
value = uint256(bytes32(executionCalldata[20:52]));
callData = executionCalldata[52:];
}
function encodeSingle(address target, uint256 value, bytes memory callData)
internal
pure
returns (bytes memory userOpCalldata)
{
userOpCalldata = abi.encodePacked(target, value, callData);
}
function doFallback2771Static(address fallbackHandler) internal view returns (bool success, bytes memory result) {
assembly {
function allocate(length) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, length))
}
let calldataPtr := allocate(calldatasize())
calldatacopy(calldataPtr, 0, calldatasize())
// The msg.sender address is shifted to the left by 12 bytes to remove the padding
// Then the address without padding is stored right after the calldata
let senderPtr := allocate(20)
mstore(senderPtr, shl(96, caller()))
// Add 20 bytes for the address appended add the end
success := staticcall(gas(), fallbackHandler, calldataPtr, add(calldatasize(), 20), 0, 0)
result := mload(0x40)
mstore(result, returndatasize()) // Store the length.
let o := add(result, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}
}
function doFallback2771Call(address target) internal returns (bool success, bytes memory result) {
assembly {
function allocate(length) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, length))
}
let calldataPtr := allocate(calldatasize())
calldatacopy(calldataPtr, 0, calldatasize())
// The msg.sender address is shifted to the left by 12 bytes to remove the padding
// Then the address without padding is stored right after the calldata
let senderPtr := allocate(20)
mstore(senderPtr, shl(96, caller()))
// Add 20 bytes for the address appended add the end
success := call(gas(), target, 0, calldataPtr, add(calldatasize(), 20), 0, 0)
result := mload(0x40)
mstore(result, returndatasize()) // Store the length.
let o := add(result, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}
}
}// 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)
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for handling ERC7579 mode and execution data.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/LibERC7579.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/main/contracts/account/utils/draft-ERC7579Utils.sol)
library LibERC7579 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Cannot decode `executionData`.
error DecodingError();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A single execution.
bytes1 internal constant CALLTYPE_SINGLE = 0x00;
/// @dev A batch of executions.
bytes1 internal constant CALLTYPE_BATCH = 0x01;
/// @dev A `delegatecall` execution.
bytes1 internal constant CALLTYPE_DELEGATECALL = 0xff;
/// @dev Default execution type that reverts on failure.
bytes1 internal constant EXECTYPE_DEFAULT = 0x00;
/// @dev Execution type that does not revert on failure.
bytes1 internal constant EXECTYPE_TRY = 0x01;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Encodes the fields into a mode.
function encodeMode(bytes1 callType, bytes1 execType, bytes4 selector, bytes22 payload)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, callType)
mstore(0x01, execType)
mstore(0x02, selector)
mstore(0x06, 0)
mstore(0x0a, payload)
result := mload(0x00)
}
}
/// @dev Returns the call type of the mode.
function getCallType(bytes32 mode) internal pure returns (bytes1) {
return bytes1(mode);
}
/// @dev Returns the call type of the mode.
function getExecType(bytes32 mode) internal pure returns (bytes1) {
return mode[1];
}
/// @dev Returns the selector of the mode.
function getSelector(bytes32 mode) internal pure returns (bytes4) {
return bytes4(bytes32(uint256(mode) << 16));
}
/// @dev Returns the payload stored in the mode.
function getPayload(bytes32 mode) internal pure returns (bytes22) {
return bytes22(bytes32(uint256(mode) << 80));
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXECUTION DATA OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Decodes a single call execution.
/// Reverts if `executionData` is not correctly encoded.
function decodeSingle(bytes calldata executionData)
internal
pure
returns (address target, uint256 value, bytes calldata data)
{
/// @solidity memory-safe-assembly
assembly {
if iszero(gt(executionData.length, 0x33)) {
mstore(0x00, 0xba597e7e) // `DecodingError()`.
revert(0x1c, 0x04)
}
target := shr(96, calldataload(executionData.offset))
value := calldataload(add(executionData.offset, 0x14))
data.offset := add(executionData.offset, 0x34)
data.length := sub(executionData.length, 0x34)
}
}
/// @dev Decodes a single call execution without bounds checks.
function decodeSingleUnchecked(bytes calldata executionData)
internal
pure
returns (address target, uint256 value, bytes calldata data)
{
/// @solidity memory-safe-assembly
assembly {
target := shr(96, calldataload(executionData.offset))
value := calldataload(add(executionData.offset, 0x14))
data.offset := add(executionData.offset, 0x34)
data.length := sub(executionData.length, 0x34)
}
}
/// @dev Decodes a single delegate execution.
/// Reverts if `executionData` is not correctly encoded.
function decodeDelegate(bytes calldata executionData)
internal
pure
returns (address target, bytes calldata data)
{
/// @solidity memory-safe-assembly
assembly {
if iszero(gt(executionData.length, 0x13)) {
mstore(0x00, 0xba597e7e) // `DecodingError()`.
revert(0x1c, 0x04)
}
target := shr(96, calldataload(executionData.offset))
data.offset := add(executionData.offset, 0x14)
data.length := sub(executionData.length, 0x14)
}
}
/// @dev Decodes a single delegate execution without bounds checks.
function decodeDelegateUnchecked(bytes calldata executionData)
internal
pure
returns (address target, bytes calldata data)
{
/// @solidity memory-safe-assembly
assembly {
target := shr(96, calldataload(executionData.offset))
data.offset := add(executionData.offset, 0x14)
data.length := sub(executionData.length, 0x14)
}
}
/// @dev Decodes a batch.
/// Reverts if `executionData` is not correctly encoded.
function decodeBatch(bytes calldata executionData)
internal
pure
returns (bytes32[] calldata pointers)
{
/// @solidity memory-safe-assembly
assembly {
let u := calldataload(executionData.offset)
if or(shr(64, u), gt(0x20, executionData.length)) {
mstore(0x00, 0xba597e7e) // `DecodingError()`.
revert(0x1c, 0x04)
}
pointers.offset := add(add(executionData.offset, u), 0x20)
pointers.length := calldataload(add(executionData.offset, u))
if pointers.length {
let e := sub(add(executionData.offset, executionData.length), 0x20)
// Perform bounds checks on the decoded `pointers`.
// Does an out-of-gas revert.
for { let i := pointers.length } 1 {} {
i := sub(i, 1)
let p := calldataload(add(pointers.offset, shl(5, i)))
let c := add(pointers.offset, p)
let q := calldataload(add(c, 0x40))
let o := add(c, q)
// forgefmt: disable-next-item
if or(shr(64, or(calldataload(o), or(p, q))),
or(gt(add(c, 0x40), e), gt(add(o, calldataload(o)), e))) {
mstore(0x00, 0xba597e7e) // `DecodingError()`.
revert(0x1c, 0x04)
}
if iszero(i) { break }
}
}
}
}
/// @dev Decodes a batch without bounds checks.
/// This function can be used in `execute`, if the validation phase has already
/// decoded the `executionData` with checks via `decodeBatch`.
function decodeBatchUnchecked(bytes calldata executionData)
internal
pure
returns (bytes32[] calldata pointers)
{
/// @solidity memory-safe-assembly
assembly {
let o := add(executionData.offset, calldataload(executionData.offset))
pointers.offset := add(o, 0x20)
pointers.length := calldataload(o)
}
}
/// @dev Decodes a batch and optional `opData`.
/// Reverts if `executionData` is not correctly encoded.
function decodeBatchAndOpData(bytes calldata executionData)
internal
pure
returns (bytes32[] calldata pointers, bytes calldata opData)
{
opData = emptyCalldataBytes();
pointers = decodeBatch(executionData);
if (hasOpData(executionData)) {
/// @solidity memory-safe-assembly
assembly {
let e := sub(add(executionData.offset, executionData.length), 0x20)
let p := calldataload(add(0x20, executionData.offset))
let q := add(executionData.offset, p)
opData.offset := add(q, 0x20)
opData.length := calldataload(q)
if or(shr(64, or(opData.length, p)), gt(add(q, opData.length), e)) {
mstore(0x00, 0xba597e7e) // `DecodingError()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Decodes a batch without bounds checks.
/// This function can be used in `execute`, if the validation phase has already
/// decoded the `executionData` with checks via `decodeBatchAndOpData`.
function decodeBatchAndOpDataUnchecked(bytes calldata executionData)
internal
pure
returns (bytes32[] calldata pointers, bytes calldata opData)
{
opData = emptyCalldataBytes();
pointers = decodeBatchUnchecked(executionData);
if (hasOpData(executionData)) {
/// @solidity memory-safe-assembly
assembly {
let q := add(executionData.offset, calldataload(add(0x20, executionData.offset)))
opData.offset := add(q, 0x20)
opData.length := calldataload(q)
}
}
}
/// @dev Returns whether the `executionData` has optional `opData`.
function hasOpData(bytes calldata executionData) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result :=
iszero(or(lt(executionData.length, 0x40), lt(calldataload(executionData.offset), 0x40)))
}
}
/// @dev Returns the `i`th execution at `pointers`, without bounds checks.
/// The bounds check is excluded as this function is intended to be called in a bounded loop.
function getExecution(bytes32[] calldata pointers, uint256 i)
internal
pure
returns (address target, uint256 value, bytes calldata data)
{
/// @solidity memory-safe-assembly
assembly {
let c := add(pointers.offset, calldataload(add(pointers.offset, shl(5, i))))
target := calldataload(c)
value := calldataload(add(c, 0x20))
let o := add(c, calldataload(add(c, 0x40)))
data.offset := add(o, 0x20)
data.length := calldataload(o)
}
}
/// @dev Reencodes `executionData` such that it has `opData` added to it.
/// Like `abi.encode(abi.decode(executionData, (Call[])), opData)`.
/// Useful for forwarding `executionData` with extra `opData`.
/// This function does not perform any check on the validity of `executionData`.
function reencodeBatch(bytes calldata executionData, bytes memory opData)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := add(0x64, mload(0x40)) // Give some space for `reencodeBatchAsExecuteCalldata`.
let s := calldataload(executionData.offset) // Offset of `calls`.
let n := sub(executionData.length, s) // Byte length of `calls`.
mstore(add(result, 0x20), 0x40) // Store the new offset of `calls`.
calldatacopy(add(result, 0x60), add(executionData.offset, s), n)
mstore(add(result, 0x40), add(0x40, n)) // Store the new offset of `opData`.
let o := add(add(result, 0x60), n) // Start offset of `opData` destination in memory.
let d := sub(opData, o) // Offset difference between `opData` source and `o`.
let end := add(mload(opData), add(0x20, o)) // End of `opData` destination in memory.
for {} 1 {} {
mstore(o, mload(add(o, d)))
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(result, sub(o, add(result, 0x20))) // Store the length of `result`.
calldatacopy(end, calldatasize(), 0x40) // Zeroize the bytes after `end`.
mstore(0x40, add(0x20, o)) // Allocate memory.
}
}
/// @dev `abi.encodeWithSignature("execute(bytes32,bytes)", mode, reencodeBatch(executionData, opData))`.
function reencodeBatchAsExecuteCalldata(
bytes32 mode,
bytes calldata executionData,
bytes memory opData
) internal pure returns (bytes memory result) {
result = reencodeBatch(executionData, opData);
/// @solidity memory-safe-assembly
assembly {
let n := mload(result)
result := sub(result, 0x64)
mstore(add(result, 0x44), 0x40) // Offset of `executionData`.
mstore(add(result, 0x24), mode)
mstore(add(result, 0x04), 0xe9ae5c53) // `execute(bytes32,bytes)`.
mstore(result, add(0x64, n))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Helper function to return empty calldata bytes.
function emptyCalldataBytes() internal pure returns (bytes calldata result) {
/// @solidity memory-safe-assembly
assembly {
result.offset := 0
result.length := 0
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import {ValidationData, PermissionId, PassFlag} from "./Types.sol";
import {IPolicy} from "../interfaces/IERC7579Modules.sol";
struct Execution {
address target;
uint256 value;
bytes callData;
}
// === for internal usage ===
struct PermissionSigMemory {
uint8 idx;
uint256 length;
ValidationData validationData;
PermissionId permission;
PassFlag flag;
IPolicy policy;
bytes permSig;
address caller;
bytes32 digest;
}
struct PermissionDisableDataFormat {
bytes[] data;
}
struct PermissionEnableDataFormat {
bytes[] data;
}
struct UserOpSigEnableDataFormat {
bytes validatorData;
bytes hookData;
bytes selectorData;
bytes enableSig;
bytes userOpSig;
}
struct SelectorDataFormat {
bytes selectorInitData;
bytes hookInitData;
}
struct SelectorDataFormatWithExecutorData {
bytes selectorInitData;
bytes hookInitData;
bytes executorHookData;
}
struct InstallValidatorDataFormat {
bytes validatorData;
bytes hookData;
bytes selectorData;
}
struct InstallExecutorDataFormat {
bytes executorData;
bytes hookData;
}
struct InstallFallbackDataFormat {
bytes selectorData;
bytes hookData;
}{
"remappings": [
"ExcessivelySafeCall/=lib/ExcessivelySafeCall/src/",
"forge-std/=lib/forge-std/src/",
"solady/=lib/solady/src/",
"solady_test/=lib/solady/test/",
"ds-test/=lib/ExcessivelySafeCall/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": "shanghai",
"viaIR": false
}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":"uint256","name":"expiration","type":"uint256"}],"name":"ExpirationTooFar","type":"error"},{"inputs":[],"name":"InvalidAccount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidOwner","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"MalleableSignature","type":"error"},{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"NotInitialized","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"expiration","type":"uint256"}],"name":"SignatureExpired","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"kernel","type":"address"},{"indexed":true,"internalType":"bytes32","name":"executionHash","type":"bytes32"}],"name":"ExecutionRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint192","name":"key","type":"uint192"},{"indexed":false,"internalType":"uint64","name":"newSequence","type":"uint64"}],"name":"NonceIncremented","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"kernel","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"OwnerRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"kernel","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"OwnerUnregistered","type":"event"},{"inputs":[{"internalType":"bytes32","name":"purposeSalt","type":"bytes32"}],"name":"deriveNonceKey","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"pure","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":[{"internalType":"address","name":"account","type":"address"},{"internalType":"ExecMode","name":"mode","type":"bytes32"},{"internalType":"bytes","name":"executionCalldata","type":"bytes"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint192","name":"key","type":"uint192"}],"name":"getNonce","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint192","name":"key","type":"uint192"}],"name":"incrementNonce","outputs":[],"stateMutability":"nonpayable","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":"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":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
610120604052348015610010575f5ffd5b50306080524660a05260608061005e604080518082018252600d81526c22a1a229a0a2bc32b1baba37b960991b602080830191909152825180840190935260018352603160f81b9083015291565b815160209283012081519183019190912060c082905260e0819052604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8152938401929092529082015246606082015230608082015260a0902061010052506100c79050565b60805160a05160c05160e051610100516111506101045f395f610a9e01525f610b5801525f610b3201525f610ae201525f610abf01526111505ff3fe60806040526004361061009a575f3560e01c8063d60b347f11610062578063d60b347f14610148578063db5c7e8814610177578063ecd0596114610197578063f2c5d61c146101b7578063f2fde38b14610230578063fa5441611461024f575f5ffd5b80630bd28e3b1461009e57806335567e1a146100bf5780636d61fe70146100fb57806384b0196e1461010e5780638a91b0e314610135575b5f5ffd5b3480156100a9575f5ffd5b506100bd6100b8366004610c59565b61029e565b005b3480156100ca575f5ffd5b506100de6100d9366004610c90565b610385565b6040516001600160401b0390911681526020015b60405180910390f35b6100bd610109366004610d07565b6103c2565b348015610119575f5ffd5b50610122610479565b6040516100f29796959493929190610d92565b6100bd610143366004610d07565b6104d9565b348015610153575f5ffd5b50610167610162366004610e28565b61055b565b60405190151581526020016100f2565b61018a610185366004610e43565b610565565b6040516100f29190610ede565b3480156101a2575f5ffd5b506101676101b1366004610f41565b60021490565b3480156101c2575f5ffd5b506102186101d1366004610f41565b604080516c22a1a229a0a2bc32b1baba37b960991b6020820152602d81018390525f9190604d0160408051601f198184030181529190528051602090910120901c92915050565b6040516001600160c01b0390911681526020016100f2565b34801561023b575f5ffd5b506100bd61024a366004610e28565b610852565b34801561025a575f5ffd5b50610286610269366004610e28565b6001600160a01b039081165f908152602081905260409020541690565b6040516001600160a01b0390911681526020016100f2565b6102a7336108fe565b6102cb5760405163f91bd6f160e01b81523360048201526024015b60405180910390fd5b335f908152602081815260408083206001600160c01b0385168452600101909152812080546001600160401b03169161030383610f6c565b82546101009290920a6001600160401b03818102199093169183160217909155335f818152602081815260408083206001600160c01b0388168085526001919091018352928190205490519416845290935090917f3c4422e7013dcec53c75bc4a839337cac5ca09bbb2aea3b2950fc04491583dd0910160405180910390a350565b6001600160a01b0382165f908152602081815260408083206001600160c01b03851684526001019091529020546001600160401b03165b92915050565b6103cb336108fe565b156103eb576040516393360fbf60e01b81523360048201526024016102c2565b5f6103f882840184610e28565b90506001600160a01b038116610421576040516349e27cff60e01b815260040160405180910390fd5b335f8181526020819052604080822080546001600160a01b0319166001600160a01b03861690811790915590519092917fa5e1f8b4009110f5525798d04ae2125421a12d0590aa52c13682ff1bd3c492ca91a3505050565b600f60f81b6060805f8080836104c7604080518082018252600d81526c22a1a229a0a2bc32b1baba37b960991b602080830191909152825180840190935260018352603160f81b9083015291565b97989097965046955030945091925090565b6104e2336108fe565b6105015760405163f91bd6f160e01b81523360048201526024016102c2565b335f8181526020819052604080822080546001600160a01b0319811690915590516001600160a01b0390911692839290917f2b019c4af52bef00865aed6c1141af64d6b25de072499d6ca81d9afcb20e86c39190a3505050565b5f6103bc826108fe565b60603068929eee149b4bd2126854036105855763ab143c065f526004601cfd5b3068929eee149b4bd21268556001600160a01b0389166105b857604051630da30f6560e31b815260040160405180910390fd5b6105c1896108fe565b6105e95760405163f91bd6f160e01b81526001600160a01b038a1660048201526024016102c2565b6105f662278d0042610f96565b84111561061957604051635170da8360e11b8152600481018590526024016102c2565b8342111561064c57604051631872770b60e01b81526001600160a01b038a166004820152602481018590526044016102c2565b610656898661091d565b6106608383610a51565b5f7f57163c36638b3a6c9b132da8051a766c3e21740a25b58e5d1671652192034d548a8a8a8a604051610694929190610fa9565b6040519081900381206106dc949392918b908b906020019586526001600160a01b0394909416602086015260408501929092526060840152608083015260a082015260c00190565b6040516020818303038152906040528051906020012090505f6106fe82610a9c565b9050808b6001600160a01b03167f8a887d1329b2ab70e3590efa71d8d5608fd795ad7798f4062b7e92bc87ab720c60405160405180910390a36001600160a01b03808c165f9081526020818152604091829020548251601f89018390048302810183019093528783529092169161079091849189908990819084018382808284375f92019190915250610bb292505050565b6001600160a01b0316816001600160a01b0316146107c157604051638baa579f60e01b815260040160405180910390fd5b6040516335a4725960e21b81526001600160a01b038d169063d691c9649034906107f3908f908f908f90600401610fb8565b5f6040518083038185885af115801561080e573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f191682016040526108369190810190611031565b3868929eee149b4bd21268559c9b505050505050505050505050565b61085b336108fe565b61087a5760405163f91bd6f160e01b81523360048201526024016102c2565b6001600160a01b0381166108a1576040516349e27cff60e01b815260040160405180910390fd5b335f8181526020819052604080822080546001600160a01b038681166001600160a01b0319831681179093559251921693909284927f73faf16c17e916c6f8862265d5560f565ca298b3a2bb70da5c3f979342a980779190a45050565b6001600160a01b039081165f9081526020819052604090205416151590565b6001600160a01b0382165f9081526020818152604080832084821c8085526001909101909252909120546001600160401b0380841691168082146109a8578467ffffffffffffffff19604085901b166001600160401b03831617604051630ec5782360e11b81526001600160a01b0390921660048301526024820152604481018590526064016102c2565b6109b3826001610f96565b6001600160a01b0386165f818152602081815260408083206001600160c01b03891680855260019182019093529220805467ffffffffffffffff19166001600160401b0395909516949094179093557f3c4422e7013dcec53c75bc4a839337cac5ca09bbb2aea3b2950fc04491583dd090610a2f908690610f96565b6040516001600160401b03909116815260200160405180910390a35050505050565b60208201357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610a975760405163f0ad0d0960e01b815260040160405180910390fd5b505050565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000030147f0000000000000000000000000000000000000000000000000000000000000000461416610b8f5750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527f000000000000000000000000000000000000000000000000000000000000000060208201527f00000000000000000000000000000000000000000000000000000000000000009181019190915246606082015230608082015260a090205b6719010000000000005f5280601a5281603a52604260182090505f603a52919050565b5f604051825160408114610bce5760418114610bef5750610c2a565b604084015160ff81901c601b016020526001600160ff1b0316606052610c02565b60608401515f1a60205260408401516060525b50835f5260208301516040526020600160805f60015afa5191505f606052806040523d610c37575b638baa579f5f526004601cfd5b5092915050565b80356001600160c01b0381168114610c54575f5ffd5b919050565b5f60208284031215610c69575f5ffd5b610c7282610c3e565b9392505050565b6001600160a01b0381168114610c8d575f5ffd5b50565b5f5f60408385031215610ca1575f5ffd5b8235610cac81610c79565b9150610cba60208401610c3e565b90509250929050565b5f5f83601f840112610cd3575f5ffd5b5081356001600160401b03811115610ce9575f5ffd5b602083019150836020828501011115610d00575f5ffd5b9250929050565b5f5f60208385031215610d18575f5ffd5b82356001600160401b03811115610d2d575f5ffd5b610d3985828601610cc3565b90969095509350505050565b5f5b83811015610d5f578181015183820152602001610d47565b50505f910152565b5f8151808452610d7e816020860160208601610d45565b601f01601f19169290920160200192915050565b60ff60f81b8816815260e060208201525f610db060e0830189610d67565b8281036040840152610dc28189610d67565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015610e17578351835260209384019390920191600101610df9565b50909b9a5050505050505050505050565b5f60208284031215610e38575f5ffd5b8135610c7281610c79565b5f5f5f5f5f5f5f5f60c0898b031215610e5a575f5ffd5b8835610e6581610c79565b97506020890135965060408901356001600160401b03811115610e86575f5ffd5b610e928b828c01610cc3565b909750955050606089013593506080890135925060a08901356001600160401b03811115610ebe575f5ffd5b610eca8b828c01610cc3565b999c989b5096995094979396929594505050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b82811015610f3557603f19878603018452610f20858351610d67565b94506020938401939190910190600101610f04565b50929695505050505050565b5f60208284031215610f51575f5ffd5b5035919050565b634e487b7160e01b5f52601160045260245ffd5b5f6001600160401b0382166001600160401b038103610f8d57610f8d610f58565b60010192915050565b808201808211156103bc576103bc610f58565b818382375f9101908152919050565b83815260406020820152816040820152818360608301375f818301606090810191909152601f909201601f1916010192915050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561102957611029610fed565b604052919050565b5f60208284031215611041575f5ffd5b81516001600160401b03811115611056575f5ffd5b8201601f81018413611066575f5ffd5b80516001600160401b0381111561107f5761107f610fed565b8060051b61108f60208201611001565b918252602081840181019290810190878411156110aa575f5ffd5b6020850192505b838310156111455782516001600160401b038111156110ce575f5ffd5b8501603f810189136110de575f5ffd5b60208101516001600160401b038111156110fa576110fa610fed565b61110d601f8201601f1916602001611001565b8181526040838301018b1015611121575f5ffd5b611132826020830160408601610d45565b84525050602092830192909101906110b1565b97965050505050505056
Deployed Bytecode
0x60806040526004361061009a575f3560e01c8063d60b347f11610062578063d60b347f14610148578063db5c7e8814610177578063ecd0596114610197578063f2c5d61c146101b7578063f2fde38b14610230578063fa5441611461024f575f5ffd5b80630bd28e3b1461009e57806335567e1a146100bf5780636d61fe70146100fb57806384b0196e1461010e5780638a91b0e314610135575b5f5ffd5b3480156100a9575f5ffd5b506100bd6100b8366004610c59565b61029e565b005b3480156100ca575f5ffd5b506100de6100d9366004610c90565b610385565b6040516001600160401b0390911681526020015b60405180910390f35b6100bd610109366004610d07565b6103c2565b348015610119575f5ffd5b50610122610479565b6040516100f29796959493929190610d92565b6100bd610143366004610d07565b6104d9565b348015610153575f5ffd5b50610167610162366004610e28565b61055b565b60405190151581526020016100f2565b61018a610185366004610e43565b610565565b6040516100f29190610ede565b3480156101a2575f5ffd5b506101676101b1366004610f41565b60021490565b3480156101c2575f5ffd5b506102186101d1366004610f41565b604080516c22a1a229a0a2bc32b1baba37b960991b6020820152602d81018390525f9190604d0160408051601f198184030181529190528051602090910120901c92915050565b6040516001600160c01b0390911681526020016100f2565b34801561023b575f5ffd5b506100bd61024a366004610e28565b610852565b34801561025a575f5ffd5b50610286610269366004610e28565b6001600160a01b039081165f908152602081905260409020541690565b6040516001600160a01b0390911681526020016100f2565b6102a7336108fe565b6102cb5760405163f91bd6f160e01b81523360048201526024015b60405180910390fd5b335f908152602081815260408083206001600160c01b0385168452600101909152812080546001600160401b03169161030383610f6c565b82546101009290920a6001600160401b03818102199093169183160217909155335f818152602081815260408083206001600160c01b0388168085526001919091018352928190205490519416845290935090917f3c4422e7013dcec53c75bc4a839337cac5ca09bbb2aea3b2950fc04491583dd0910160405180910390a350565b6001600160a01b0382165f908152602081815260408083206001600160c01b03851684526001019091529020546001600160401b03165b92915050565b6103cb336108fe565b156103eb576040516393360fbf60e01b81523360048201526024016102c2565b5f6103f882840184610e28565b90506001600160a01b038116610421576040516349e27cff60e01b815260040160405180910390fd5b335f8181526020819052604080822080546001600160a01b0319166001600160a01b03861690811790915590519092917fa5e1f8b4009110f5525798d04ae2125421a12d0590aa52c13682ff1bd3c492ca91a3505050565b600f60f81b6060805f8080836104c7604080518082018252600d81526c22a1a229a0a2bc32b1baba37b960991b602080830191909152825180840190935260018352603160f81b9083015291565b97989097965046955030945091925090565b6104e2336108fe565b6105015760405163f91bd6f160e01b81523360048201526024016102c2565b335f8181526020819052604080822080546001600160a01b0319811690915590516001600160a01b0390911692839290917f2b019c4af52bef00865aed6c1141af64d6b25de072499d6ca81d9afcb20e86c39190a3505050565b5f6103bc826108fe565b60603068929eee149b4bd2126854036105855763ab143c065f526004601cfd5b3068929eee149b4bd21268556001600160a01b0389166105b857604051630da30f6560e31b815260040160405180910390fd5b6105c1896108fe565b6105e95760405163f91bd6f160e01b81526001600160a01b038a1660048201526024016102c2565b6105f662278d0042610f96565b84111561061957604051635170da8360e11b8152600481018590526024016102c2565b8342111561064c57604051631872770b60e01b81526001600160a01b038a166004820152602481018590526044016102c2565b610656898661091d565b6106608383610a51565b5f7f57163c36638b3a6c9b132da8051a766c3e21740a25b58e5d1671652192034d548a8a8a8a604051610694929190610fa9565b6040519081900381206106dc949392918b908b906020019586526001600160a01b0394909416602086015260408501929092526060840152608083015260a082015260c00190565b6040516020818303038152906040528051906020012090505f6106fe82610a9c565b9050808b6001600160a01b03167f8a887d1329b2ab70e3590efa71d8d5608fd795ad7798f4062b7e92bc87ab720c60405160405180910390a36001600160a01b03808c165f9081526020818152604091829020548251601f89018390048302810183019093528783529092169161079091849189908990819084018382808284375f92019190915250610bb292505050565b6001600160a01b0316816001600160a01b0316146107c157604051638baa579f60e01b815260040160405180910390fd5b6040516335a4725960e21b81526001600160a01b038d169063d691c9649034906107f3908f908f908f90600401610fb8565b5f6040518083038185885af115801561080e573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f191682016040526108369190810190611031565b3868929eee149b4bd21268559c9b505050505050505050505050565b61085b336108fe565b61087a5760405163f91bd6f160e01b81523360048201526024016102c2565b6001600160a01b0381166108a1576040516349e27cff60e01b815260040160405180910390fd5b335f8181526020819052604080822080546001600160a01b038681166001600160a01b0319831681179093559251921693909284927f73faf16c17e916c6f8862265d5560f565ca298b3a2bb70da5c3f979342a980779190a45050565b6001600160a01b039081165f9081526020819052604090205416151590565b6001600160a01b0382165f9081526020818152604080832084821c8085526001909101909252909120546001600160401b0380841691168082146109a8578467ffffffffffffffff19604085901b166001600160401b03831617604051630ec5782360e11b81526001600160a01b0390921660048301526024820152604481018590526064016102c2565b6109b3826001610f96565b6001600160a01b0386165f818152602081815260408083206001600160c01b03891680855260019182019093529220805467ffffffffffffffff19166001600160401b0395909516949094179093557f3c4422e7013dcec53c75bc4a839337cac5ca09bbb2aea3b2950fc04491583dd090610a2f908690610f96565b6040516001600160401b03909116815260200160405180910390a35050505050565b60208201357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610a975760405163f0ad0d0960e01b815260040160405180910390fd5b505050565b7f35f09d4e7a5cd124b6336d354865df0da65e5eb3941da12db85907d845f36cb97f000000000000000000000000ec2c96e75b09e29b66bf2ee5c37fa749ef9aa7c730147f0000000000000000000000000000000000000000000000000000000000000001461416610b8f5750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527f624e42c426ec632a75570ab2c59adc8c1a61766221f44c6808aa2e0f2d6d57d060208201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69181019190915246606082015230608082015260a090205b6719010000000000005f5280601a5281603a52604260182090505f603a52919050565b5f604051825160408114610bce5760418114610bef5750610c2a565b604084015160ff81901c601b016020526001600160ff1b0316606052610c02565b60608401515f1a60205260408401516060525b50835f5260208301516040526020600160805f60015afa5191505f606052806040523d610c37575b638baa579f5f526004601cfd5b5092915050565b80356001600160c01b0381168114610c54575f5ffd5b919050565b5f60208284031215610c69575f5ffd5b610c7282610c3e565b9392505050565b6001600160a01b0381168114610c8d575f5ffd5b50565b5f5f60408385031215610ca1575f5ffd5b8235610cac81610c79565b9150610cba60208401610c3e565b90509250929050565b5f5f83601f840112610cd3575f5ffd5b5081356001600160401b03811115610ce9575f5ffd5b602083019150836020828501011115610d00575f5ffd5b9250929050565b5f5f60208385031215610d18575f5ffd5b82356001600160401b03811115610d2d575f5ffd5b610d3985828601610cc3565b90969095509350505050565b5f5b83811015610d5f578181015183820152602001610d47565b50505f910152565b5f8151808452610d7e816020860160208601610d45565b601f01601f19169290920160200192915050565b60ff60f81b8816815260e060208201525f610db060e0830189610d67565b8281036040840152610dc28189610d67565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015610e17578351835260209384019390920191600101610df9565b50909b9a5050505050505050505050565b5f60208284031215610e38575f5ffd5b8135610c7281610c79565b5f5f5f5f5f5f5f5f60c0898b031215610e5a575f5ffd5b8835610e6581610c79565b97506020890135965060408901356001600160401b03811115610e86575f5ffd5b610e928b828c01610cc3565b909750955050606089013593506080890135925060a08901356001600160401b03811115610ebe575f5ffd5b610eca8b828c01610cc3565b999c989b5096995094979396929594505050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b82811015610f3557603f19878603018452610f20858351610d67565b94506020938401939190910190600101610f04565b50929695505050505050565b5f60208284031215610f51575f5ffd5b5035919050565b634e487b7160e01b5f52601160045260245ffd5b5f6001600160401b0382166001600160401b038103610f8d57610f8d610f58565b60010192915050565b808201808211156103bc576103bc610f58565b818382375f9101908152919050565b83815260406020820152816040820152818360608301375f818301606090810191909152601f909201601f1916010192915050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561102957611029610fed565b604052919050565b5f60208284031215611041575f5ffd5b81516001600160401b03811115611056575f5ffd5b8201601f81018413611066575f5ffd5b80516001600160401b0381111561107f5761107f610fed565b8060051b61108f60208201611001565b918252602081840181019290810190878411156110aa575f5ffd5b6020850192505b838310156111455782516001600160401b038111156110ce575f5ffd5b8501603f810189136110de575f5ffd5b60208101516001600160401b038111156110fa576110fa610fed565b61110d601f8201601f1916602001611001565b8181526040838301018b1015611121575f5ffd5b611132826020830160408601610d45565b84525050602092830192909101906110b1565b97965050505050505056
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.