Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 129073574 | 424 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
K1Validator
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 999 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { ECDSA } from "solady/utils/ECDSA.sol"; import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; import { ERC7739Validator } from "erc7739Validator/ERC7739Validator.sol"; import { IValidator } from "../../interfaces/modules/IValidator.sol"; import { EnumerableSet } from "../../lib/EnumerableSet4337.sol"; import { MODULE_TYPE_VALIDATOR, VALIDATION_SUCCESS, VALIDATION_FAILED } from "../../types/Constants.sol"; /// @title Nexus - K1Validator (ECDSA) /// @notice Validator module for smart accounts, verifying user operation signatures /// based on the K1 curve (secp256k1), a widely used ECDSA algorithm. /// @dev Implements secure ownership validation by checking signatures against registered /// owners. This module supports ERC-7579 and ERC-4337 standards, ensuring only the /// legitimate owner of a smart account can authorize transactions. /// Implements ERC-7739 /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady contract K1Validator is IValidator, ERC7739Validator { using ECDSA for bytes32; using EnumerableSet for EnumerableSet.AddressSet; /*////////////////////////////////////////////////////////////////////////// CONSTANTS & STORAGE //////////////////////////////////////////////////////////////////////////*/ /// @notice Mapping of smart account addresses to their respective owner addresses mapping(address => address) public smartAccountOwners; EnumerableSet.AddressSet private _safeSenders; /// @notice Error to indicate that no owner was provided during installation error NoOwnerProvided(); /// @notice Error to indicate that the new owner cannot be the zero address error ZeroAddressNotAllowed(); /// @notice Error to indicate the module is already initialized error ModuleAlreadyInitialized(); /// @notice Error to indicate that the new owner cannot be a contract address error NewOwnerIsContract(); /// @notice Error to indicate that the owner cannot be the zero address error OwnerCannotBeZeroAddress(); /// @notice Error to indicate that the data length is invalid error InvalidDataLength(); /*////////////////////////////////////////////////////////////////////////// CONFIG //////////////////////////////////////////////////////////////////////////*/ /** * Initialize the module with the given data * * @param data The data to initialize the module with */ function onInstall(bytes calldata data) external override { require(data.length != 0, NoOwnerProvided()); require(!_isInitialized(msg.sender), ModuleAlreadyInitialized()); address newOwner = address(bytes20(data[:20])); require(newOwner != address(0), OwnerCannotBeZeroAddress()); require(!_isContract(newOwner), NewOwnerIsContract()); smartAccountOwners[msg.sender] = newOwner; if (data.length > 20) { _fillSafeSenders(data[20:]); } } /** * De-initialize the module with the given data */ function onUninstall(bytes calldata) external override { delete smartAccountOwners[msg.sender]; _safeSenders.removeAll(msg.sender); } /// @notice Transfers ownership of the validator to a new owner /// @param newOwner The address of the new owner function transferOwnership(address newOwner) external { require(newOwner != address(0), ZeroAddressNotAllowed()); require(!_isContract(newOwner), NewOwnerIsContract()); smartAccountOwners[msg.sender] = newOwner; } /// @notice Adds a safe sender to the _safeSenders list for the smart account function addSafeSender(address sender) external { _safeSenders.add(msg.sender, sender); } /// @notice Removes a safe sender from the _safeSenders list for the smart account function removeSafeSender(address sender) external { _safeSenders.remove(msg.sender, sender); } /// @notice Checks if a sender is in the _safeSenders list for the smart account function isSafeSender(address sender, address smartAccount) external view returns (bool) { return _safeSenders.contains(smartAccount, sender); } /** * Check if the module is initialized * @param smartAccount The smart account to check * * @return true if the module is initialized, false otherwise */ function isInitialized(address smartAccount) external view returns (bool) { return _isInitialized(smartAccount); } /*////////////////////////////////////////////////////////////////////////// MODULE LOGIC //////////////////////////////////////////////////////////////////////////*/ /** * Validates PackedUserOperation * * @param userOp UserOperation to be validated * @param userOpHash Hash of the UserOperation to be validated * * @return uint256 the result of the signature validation, which can be: * - 0 if the signature is valid * - 1 if the signature is invalid * - <20-byte> aggregatorOrSigFail, <6-byte> validUntil and <6-byte> validAfter (see ERC-4337 * for more details) */ function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external view override returns (uint256) { return _validateSignatureForOwner(smartAccountOwners[userOp.sender], userOpHash, userOp.signature) ? VALIDATION_SUCCESS : VALIDATION_FAILED; } /** * Validates an ERC-1271 signature * @dev implements signature malleability prevention * see: https://eips.ethereum.org/EIPS/eip-1271#reference-implementation * Please note, that this prevention does not protect against replay attacks in general * So the protocol using ERC-1271 should make sure hash is replay-safe. * * @param sender The sender of the ERC-1271 call to the account * @param hash The hash of the message * @param signature The signature of the message * * @return sigValidationResult the result of the signature validation, which can be: * - EIP1271_SUCCESS if the signature is valid * - EIP1271_FAILED if the signature is invalid * - 0x7739000X if this is the ERC-7739 support detection request. * Where X is the version of the ERC-7739 support. */ function isValidSignatureWithSender( address sender, bytes32 hash, bytes calldata signature ) external view virtual override returns (bytes4) { return _erc1271IsValidSignatureWithSender(sender, hash, _erc1271UnwrapSignature(signature)); } /// @notice ISessionValidator interface for smart session /// @param hash The hash of the data to validate /// @param sig The signature data /// @param data The data to validate against (owner address in this case) function validateSignatureWithData(bytes32 hash, bytes calldata sig, bytes calldata data) external view returns (bool validSig) { require(data.length == 20, InvalidDataLength()); address owner = address(bytes20(data[0:20])); return _validateSignatureForOwner(owner, hash, sig); } /*////////////////////////////////////////////////////////////////////////// METADATA //////////////////////////////////////////////////////////////////////////*/ /// @notice Returns the name of the module /// @return The name of the module function name() external pure returns (string memory) { return "K1Validator"; } /// @notice Returns the version of the module /// @return The version of the module function version() external pure returns (string memory) { return "1.0.1"; } /// @notice Checks if the module is of the specified type /// @param typeId The type ID to check /// @return True if the module is of the specified type, false otherwise function isModuleType(uint256 typeId) external pure returns (bool) { return typeId == MODULE_TYPE_VALIDATOR; } /*////////////////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////////////////*/ /// @notice Recovers the signer from a signature /// @param hash The hash of the data to validate /// @param signature The signature data /// @return The recovered signer address /// @notice tryRecover returns address(0) on invalid signature function _recoverSigner(bytes32 hash, bytes calldata signature) internal view returns (address) { return hash.tryRecover(signature); } /// @dev Returns whether the `hash` and `signature` are valid. /// Obtains the authorized signer's credentials and calls some /// module's specific internal function to validate the signature /// against credentials. function _erc1271IsValidSignatureNowCalldata(bytes32 hash, bytes calldata signature) internal view override returns (bool) { // call custom internal function to validate the signature against credentials return _validateSignatureForOwner(smartAccountOwners[msg.sender], hash, signature); } /// @dev Returns whether the `sender` is considered safe, such /// that we don't need to use the nested EIP-712 workflow. /// See: https://mirror.xyz/curiousapple.eth/pFqAdW2LiJ-6S4sg_u1z08k4vK6BCJ33LcyXpnNb8yU // The canonical `MulticallerWithSigner` at 0x000000000000D9ECebf3C23529de49815Dac1c4c // is known to include the account in the hash to be signed. // msg.sender = Smart Account // sender = 1271 og request sender function _erc1271CallerIsSafe(address sender) internal view virtual override returns (bool) { return (sender == 0x000000000000D9ECebf3C23529de49815Dac1c4c || // MulticallerWithSigner sender == msg.sender || // Smart Account. Assume smart account never sends non safe eip-712 struct _safeSenders.contains(msg.sender, sender)); // check if sender is in _safeSenders for the Smart Account } /// @notice Internal method that does the job of validating the signature via ECDSA (secp256k1) /// @param owner The address of the owner /// @param hash The hash of the data to validate /// @param signature The signature data function _validateSignatureForOwner(address owner, bytes32 hash, bytes calldata signature) internal view returns (bool) { // verify signer // owner can not be zero address in this contract if (_recoverSigner(hash, signature) == owner) return true; if (_recoverSigner(hash.toEthSignedMessageHash(), signature) == owner) return true; return false; } // @notice Fills the _safeSenders list from the given data function _fillSafeSenders(bytes calldata data) private { for (uint256 i; i < data.length / 20; i++) { _safeSenders.add(msg.sender, address(bytes20(data[20 * i:20 * (i + 1)]))); } } /// @notice Checks if the smart account is initialized with an owner /// @param smartAccount The address of the smart account /// @return True if the smart account has an owner, false otherwise function _isInitialized(address smartAccount) private view returns (bool) { return smartAccountOwners[smartAccount] != address(0); } /// @notice Checks if the address is a contract /// @param account The address to check /// @return True if the address is a contract, false otherwise function _isContract(address account) private view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; } }
// 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: 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 maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters.
* @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;
bytes paymasterAndData;
bytes signature;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
interface IERC5267 {
function eip712Domain() external view returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
/// @title ERC-7739: Nested Typed Data Sign Support for ERC-7579 Validators
abstract contract ERC7739Validator {
error InvalidSignature();
/// @dev `keccak256("PersonalSign(bytes prefixed)")`.
bytes32 internal constant _PERSONAL_SIGN_TYPEHASH = 0x983e65e5148e570cd828ead231ee759a8d7958721a768f93bc4483ba005c32de;
bytes32 internal constant _DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
bytes4 internal constant SUPPORTS_ERC7739 = 0x77390001;
/*//////////////////////////////////////////////////////////////////////////
INTERNAL
//////////////////////////////////////////////////////////////////////////*/
/// @dev Returns whether the `signature` is valid for the `hash.
/// Use this in your validator's `isValidSignatureWithSender` implementation.
function _erc1271IsValidSignatureWithSender(address sender, bytes32 hash, bytes calldata signature)
internal
view
virtual
returns (bytes4)
{
// detection request
// this check only takes 17 gas units
// in theory, it can be moved out of this function so it doesn't apply to every
// isValidSignatureWithSender() call, but it would require an additional standard
// interface for SA to check if the IValidator supports ERC-7739
// while isValidSignatureWithSender() is specified by ERC-7579, so
// it makes sense to use it in SA to check if the validator supports ERC-7739
unchecked {
if (signature.length == uint256(0)) {
// Forces the compiler to optimize for smaller bytecode size.
if (uint256(hash) == ~signature.length / 0xffff * 0x7739)
return SUPPORTS_ERC7739;
}
}
// sig malleability prevention
bytes32 s;
assembly {
// same as `s := mload(add(signature, 0x40))` but for calldata
s := calldataload(add(signature.offset, 0x20))
}
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert InvalidSignature();
}
bool success = _erc1271IsValidSignatureViaSafeCaller(sender, hash, signature)
|| _erc1271IsValidSignatureViaNestedEIP712(hash, signature)
|| _erc1271IsValidSignatureViaRPC(hash, signature);
bytes4 sigValidationResult;
assembly {
// `success ? bytes4(keccak256("isValidSignature(bytes32,bytes)")) : 0xffffffff`.
// We use `0xffffffff` for invalid, in convention with the reference implementation.
sigValidationResult := shl(224, or(0x1626ba7e, sub(0, iszero(success))))
}
return sigValidationResult;
}
/// @dev Returns whether the `msg.sender` is considered safe, such
/// that we don't need to use the nested EIP-712 workflow.
/// Override to return true for more callers.
/// See: https://mirror.xyz/curiousapple.eth/pFqAdW2LiJ-6S4sg_u1z08k4vK6BCJ33LcyXpnNb8yU
function _erc1271CallerIsSafe(address sender) internal view virtual returns (bool) {
// The canonical `MulticallerWithSigner` at 0x000000000000D9ECebf3C23529de49815Dac1c4c
// is known to include the account in the hash to be signed.
return sender == 0x000000000000D9ECebf3C23529de49815Dac1c4c;
}
/// @dev Returns whether the `hash` and `signature` are valid.
/// Obtains the authorized signer's credentials and calls some
/// module's specific internal function to validate the signature
/// against credentials.
/// Override for your module's custom logic.
function _erc1271IsValidSignatureNowCalldata(bytes32 hash, bytes calldata signature)
internal
view
virtual
returns (bool);
/// @dev Unwraps and returns the signature.
function _erc1271UnwrapSignature(bytes calldata signature)
internal
view
virtual
returns (bytes calldata result)
{
result = signature;
/// @solidity memory-safe-assembly
assembly {
// Unwraps the ERC6492 wrapper if it exists.
// See: https://eips.ethereum.org/EIPS/eip-6492
if eq(
calldataload(add(result.offset, sub(result.length, 0x20))),
mul(0x6492, div(not(shr(address(), address())), 0xffff)) // `0x6492...6492`.
) {
let o := add(result.offset, calldataload(add(result.offset, 0x40)))
result.length := calldataload(o)
result.offset := add(o, 0x20)
}
}
}
/// @dev Performs the signature validation without nested EIP-712 if the caller is
/// a safe caller. A safe caller must include the address of this account in the hash.
function _erc1271IsValidSignatureViaSafeCaller(address sender, bytes32 hash, bytes calldata signature)
internal
view
virtual
returns (bool result)
{
if (_erc1271CallerIsSafe(sender)) result = _erc1271IsValidSignatureNowCalldata(hash, signature);
}
/// @dev ERC1271 signature validation (Nested EIP-712 workflow).
///
/// This uses ECDSA recovery by default (see: `_erc1271IsValidSignatureNowCalldata`).
/// It also uses a nested EIP-712 approach to prevent signature replays when a single EOA
/// owns multiple smart contract accounts,
/// while still enabling wallet UIs (e.g. Metamask) to show the EIP-712 values.
///
/// Crafted for phishing resistance, efficiency, flexibility.
/// __________________________________________________________________________________________
///
/// Glossary:
///
/// - `APP_DOMAIN_SEPARATOR`: The domain separator of the `hash` passed in by the application.
/// Provided by the front end. Intended to be the domain separator of the contract
/// that will call `isValidSignature` on this account.
///
/// - `ACCOUNT_DOMAIN_SEPARATOR`: The domain separator of this account.
/// See: `EIP712._domainSeparator()`.
/// __________________________________________________________________________________________
///
/// For the `TypedDataSign` workflow, the final hash will be:
/// ```
/// keccak256(\x19\x01 ‖ APP_DOMAIN_SEPARATOR ‖
/// hashStruct(TypedDataSign({
/// contents: hashStruct(originalStruct),
/// name: keccak256(bytes(eip712Domain().name)),
/// version: keccak256(bytes(eip712Domain().version)),
/// chainId: eip712Domain().chainId,
/// verifyingContract: eip712Domain().verifyingContract,
/// salt: eip712Domain().salt
/// }))
/// )
/// ```
/// where `‖` denotes the concatenation operator for bytes.
/// The order of the fields is important: `contents` comes before `name`.
///
/// The signature will be `r ‖ s ‖ v ‖ APP_DOMAIN_SEPARATOR ‖
/// contents ‖ contentsDescription ‖ uint16(contentsDescription.length)`,
/// where:
/// - `contents` is the bytes32 struct hash of the original struct.
/// - `contentsDescription` can be either:
/// a) `contentsType` (implicit mode)
/// where `contentsType` starts with `contentsName`.
/// b) `contentsType ‖ contentsName` (explicit mode)
/// where `contentsType` may not necessarily start with `contentsName`.
///
/// The `APP_DOMAIN_SEPARATOR` and `contents` will be used to verify if `hash` is indeed correct.
/// __________________________________________________________________________________________
///
/// For the `PersonalSign` workflow, the final hash will be:
/// ```
/// keccak256(\x19\x01 ‖ ACCOUNT_DOMAIN_SEPARATOR ‖
/// hashStruct(PersonalSign({
/// prefixed: keccak256(bytes(\x19Ethereum Signed Message:\n ‖
/// base10(bytes(someString).length) ‖ someString))
/// }))
/// )
/// ```
/// where `‖` denotes the concatenation operator for bytes.
///
/// The `PersonalSign` type hash will be `keccak256("PersonalSign(bytes prefixed)")`.
/// The signature will be `r ‖ s ‖ v`.
/// __________________________________________________________________________________________
///
/// For demo and typescript code, see:
/// - https://github.com/junomonster/nested-eip-712
/// - https://github.com/frangio/eip712-wrapper-for-eip1271
///
/// Their nomenclature may differ from ours, although the high-level idea is similar.
///
/// Of course, if you have control over the codebase of the wallet client(s) too,
/// you can choose a more minimalistic signature scheme like
/// `keccak256(abi.encode(address(this), hash))` instead of all these acrobatics.
/// All these are just for widespread out-of-the-box compatibility with other wallet clients.
/// We want to create bazaars, not walled castles.
/// And we'll use push the Turing Completeness of the EVM to the limits to do so.
function _erc1271IsValidSignatureViaNestedEIP712(bytes32 hash, bytes calldata signature)
internal
view
virtual
returns (bool result)
{
//bytes32 t = _typedDataSignFieldsForAccount(msg.sender);
uint256 t = uint256(uint160(address(this)));
// Forces the compiler to pop the variables after the scope, avoiding stack-too-deep.
if (t != uint256(0)) {
(
,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
) = IERC5267(msg.sender).eip712Domain();
/// @solidity memory-safe-assembly
assembly {
t := mload(0x40) // Grab the free memory pointer.
// Skip 2 words for the `typedDataSignTypehash` and `contents` struct hash.
mstore(add(t, 0x40), keccak256(add(name, 0x20), mload(name)))
mstore(add(t, 0x60), keccak256(add(version, 0x20), mload(version)))
mstore(add(t, 0x80), chainId)
mstore(add(t, 0xa0), shr(96, shl(96, verifyingContract)))
mstore(add(t, 0xc0), salt)
mstore(0x40, add(t, 0xe0)) // Allocate the memory.
}
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
// `c` is `contentsDescription.length`, which is stored in the last 2 bytes of the signature.
let c := shr(240, calldataload(add(signature.offset, sub(signature.length, 2))))
for {} 1 {} {
let l := add(0x42, c) // Total length of appended data (32 + 32 + c + 2).
let o := add(signature.offset, sub(signature.length, l)) // Offset of appended data.
mstore(0x00, 0x1901) // Store the "\x19\x01" prefix.
calldatacopy(0x20, o, 0x40) // Copy the `APP_DOMAIN_SEPARATOR` and `contents` struct hash.
// Use the `PersonalSign` workflow if the reconstructed hash doesn't match,
// or if the appended data is invalid, i.e.
// `appendedData.length > signature.length || contentsDescription.length == 0`.
if or(xor(keccak256(0x1e, 0x42), hash), or(lt(signature.length, l), iszero(c))) {
t := 0 // Set `t` to 0, denoting that we need to `hash = _hashTypedData(hash)`.
mstore(t, _PERSONAL_SIGN_TYPEHASH)
mstore(0x20, hash) // Store the `prefixed`.
hash := keccak256(t, 0x40) // Compute the `PersonalSign` struct hash.
break
}
// Else, use the `TypedDataSign` workflow.
// `TypedDataSign({ContentsName} contents,string name,...){ContentsType}`.
mstore(m, "TypedDataSign(") // Store the start of `TypedDataSign`'s type encoding.
let p := add(m, 0x0e) // Advance 14 bytes to skip "TypedDataSign(".
calldatacopy(p, add(o, 0x40), c) // Copy `contentsName`, optimistically.
mstore(add(p, c), 40) // Store a '(' after the end.
if iszero(eq(byte(0, mload(sub(add(p, c), 1))), 41)) {
let e := 0 // Length of `contentsName` in explicit mode.
for { let q := sub(add(p, c), 1) } 1 {} {
e := add(e, 1) // Scan backwards until we encounter a ')'.
if iszero(gt(lt(e, c), eq(byte(0, mload(sub(q, e))), 41))) { break }
}
c := sub(c, e) // Truncate `contentsDescription` to `contentsType`.
calldatacopy(p, add(add(o, 0x40), c), e) // Copy `contentsName`.
mstore8(add(p, e), 40) // Store a '(' exactly right after the end.
}
// `d & 1 == 1` means that `contentsName` is invalid.
let d := shr(byte(0, mload(p)), 0x7fffffe000000000000010000000000) // Starts with `[a-z(]`.
// Advance `p` until we encounter '('.
for {} iszero(eq(byte(0, mload(p)), 40)) { p := add(p, 1) } {
d := or(shr(byte(0, mload(p)), 0x120100000001), d) // Has a byte in ", )\x00".
}
mstore(p, " contents,string name,string") // Store the rest of the encoding.
mstore(add(p, 0x1c), " version,uint256 chainId,address")
mstore(add(p, 0x3c), " verifyingContract,bytes32 salt)")
p := add(p, 0x5c)
calldatacopy(p, add(o, 0x40), c) // Copy `contentsType`.
// Fill in the missing fields of the `TypedDataSign`.
calldatacopy(t, o, 0x40) // Copy the `contents` struct hash to `add(t, 0x20)`.
mstore(t, keccak256(m, sub(add(p, c), m))) // Store `typedDataSignTypehash`.
// The "\x19\x01" prefix is already at 0x00.
// `APP_DOMAIN_SEPARATOR` is already at 0x20.
mstore(0x40, keccak256(t, 0xe0)) // `hashStruct(typedDataSign)`.
// Compute the final hash, corrupted if `contentsName` is invalid.
hash := keccak256(0x1e, add(0x42, and(1, d)))
signature.length := sub(signature.length, l) // Truncate the signature.
break
}
mstore(0x40, m) // Restore the free memory pointer.
}
if (t == uint256(0)) hash = _hashTypedDataForAccount(msg.sender, hash); // `PersonalSign` workflow.
result = _erc1271IsValidSignatureNowCalldata(hash, signature);
}
/// @dev Performs the signature validation without nested EIP-712 to allow for easy sign ins.
/// This function must always return false or revert if called on-chain.
function _erc1271IsValidSignatureViaRPC(bytes32 hash, bytes calldata signature)
internal
view
virtual
returns (bool result)
{
// Non-zero gasprice is a heuristic to check if a call is on-chain,
// but we can't fully depend on it because it can be manipulated.
// See: https://x.com/NoahCitron/status/1580359718341484544
if (tx.gasprice == uint256(0)) {
/// @solidity memory-safe-assembly
assembly {
mstore(gasprice(), gasprice())
// See: https://gist.github.com/Vectorized/3c9b63524d57492b265454f62d895f71
let b := 0x000000000000378eDCD5B5B0A24f5342d8C10485 // Basefee contract,
pop(staticcall(0xffff, b, codesize(), gasprice(), gasprice(), 0x20))
// If `gasprice < basefee`, the call cannot be on-chain, and we can skip the gas burn.
if iszero(mload(gasprice())) {
let m := mload(0x40) // Cache the free memory pointer.
mstore(gasprice(), 0x1626ba7e) // `isValidSignature(bytes32,bytes)`.
mstore(0x20, b) // Recycle `b` to denote if we need to burn gas.
mstore(0x40, 0x40)
let gasToBurn := or(add(0xffff, gaslimit()), gaslimit())
// Burns gas computationally efficiently. Also, requires that `gas > gasToBurn`.
if or(eq(hash, b), lt(gas(), gasToBurn)) { invalid() }
// Make a call to this with `b`, efficiently burning the gas provided.
// No valid transaction can consume more than the gaslimit.
// See: https://ethereum.github.io/yellowpaper/paper.pdf
// Most RPCs perform calls with a gas budget greater than the gaslimit.
pop(staticcall(gasToBurn, address(), 0x1c, 0x64, gasprice(), gasprice()))
mstore(0x40, m) // Restore the free memory pointer.
}
}
result = _erc1271IsValidSignatureNowCalldata(hash, signature);
}
}
/// @notice Hashes typed data according to eip-712
/// Uses account's domain separator
/// @param account the smart account, who's domain separator will be used
/// @param structHash the typed data struct hash
function _hashTypedDataForAccount(address account, bytes32 structHash) private view returns (bytes32 digest) {
(
/*bytes1 fields*/,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
/*bytes32 salt*/,
/*uint256[] memory extensions*/
) = IERC5267(account).eip712Domain();
/// @solidity memory-safe-assembly
assembly {
//Rebuild domain separator out of 712 domain
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), keccak256(add(name, 0x20), mload(name))) // Name hash.
mstore(add(m, 0x40), keccak256(add(version, 0x20), mload(version))) // Version hash.
mstore(add(m, 0x60), chainId)
mstore(add(m, 0x80), verifyingContract)
digest := keccak256(m, 0xa0) //domain separator
// Hash typed data
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)
}
}
/// @dev Backwards compatibility stuff
/// For automatic detection that the smart account supports the nested EIP-712 workflow.
/// By default, it returns `bytes32(bytes4(keccak256("supportsNestedTypedDataSign()")))`,
/// denoting support for the default behavior, as implemented in
/// `_erc1271IsValidSignatureViaNestedEIP712`, which is called in `isValidSignature`.
/// Future extensions should return a different non-zero `result` to denote different behavior.
/// This method intentionally returns bytes32 to allow freedom for future extensions.
function supportsNestedTypedDataSign() public view virtual returns (bytes32 result) {
result = bytes4(0xd620c85a);
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; import { IModule } from "./IModule.sol"; /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IValidator is IModule { /// @notice Validates a user operation as per ERC-4337 standard requirements. /// @dev Should ensure that the signature and nonce are verified correctly before the transaction is allowed to proceed. /// The function returns a status code indicating validation success or failure. /// @param userOp The user operation containing transaction details to be validated. /// @param userOpHash The hash of the user operation data, used for verifying the signature. /// @return status The result of the validation process, typically indicating success or the type of failure. function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); /// @notice Verifies a signature against a hash, using the sender's address as a contextual check. /// @dev Used to confirm the validity of a signature against the specific conditions set by the sender. /// @param sender The address from which the operation was initiated, adding an additional layer of validation against the signature. /// @param hash The hash of the data signed. /// @param data The signature data to validate. /// @return magicValue A bytes4 value that corresponds to the ERC-1271 standard, indicating the validity of the signature. function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data) external view returns (bytes4); }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import "./AssociatedArrayLib.sol";
/**
* Fork of OZ's EnumerableSet that makes all storage access ERC-4337 compliant via associated storage
* @author zeroknots.eth (rhinestone)
*/
library EnumerableSet {
using AssociatedArrayLib for AssociatedArrayLib.Bytes32Array;
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
AssociatedArrayLib.Bytes32Array _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => mapping(address account => uint256)) _positions;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
// AddressSet
struct AddressSet {
Set _inner;
}
// UintSet
struct UintSet {
Set _inner;
}
function _removeAll(Set storage set, address account) internal {
// get length of the array
uint256 len = _length(set, account);
for (uint256 i = 1; i <= len; i++) {
// get last value
bytes32 value = _at(set, account, len - i);
_remove(set, account, value);
}
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, address account, bytes32 value) internal returns (bool) {
return _add(set._inner, account, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, address account, bytes32 value) internal returns (bool) {
return _remove(set._inner, account, value);
}
function removeAll(Bytes32Set storage set, address account) internal {
return _removeAll(set._inner, account);
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address account, address value) internal returns (bool) {
return _add(set._inner, account, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address account, address value) internal returns (bool) {
return _remove(set._inner, account, bytes32(uint256(uint160(value))));
}
function removeAll(AddressSet storage set, address account) internal {
return _removeAll(set._inner, account);
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, address account, uint256 value) internal returns (bool) {
return _add(set._inner, account, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, address account, uint256 value) internal returns (bool) {
return _remove(set._inner, account, bytes32(value));
}
function removeAll(UintSet storage set, address account) internal {
return _removeAll(set._inner, account);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, address account, bytes32 value) internal view returns (bool) {
return _contains(set._inner, account, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set, address account) internal view returns (uint256) {
return _length(set._inner, account);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, address account, uint256 index) internal view returns (bytes32) {
return _at(set._inner, account, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set, address account) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner, account);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address account, address value) internal view returns (bool) {
return _contains(set._inner, account, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set, address account) internal view returns (uint256) {
return _length(set._inner, account);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, address account, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, account, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set, address account) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner, account);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, address account, uint256 value) internal view returns (bool) {
return _contains(set._inner, account, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set, address account) internal view returns (uint256) {
return _length(set._inner, account);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, address account, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, account, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set, address account) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner, account);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, address account, bytes32 value) private returns (bool) {
if (!_contains(set, account, value)) {
set._values.push(account, value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value][account] = set._values.length(account);
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, address account, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value][account];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length(account) - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values.get(account, lastIndex);
// Move the lastValue to the index where the value to delete is
set._values.set(account, valueIndex, lastValue);
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue][account] = position;
}
// Delete the slot where the moved value was stored
set._values.pop(account);
// Delete the tracked position for the deleted slot
delete set._positions[value][account];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, address account, bytes32 value) private view returns (bool) {
return set._positions[value][account] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set, address account) private view returns (uint256) {
return set._values.length(account);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, address account, uint256 index) private view returns (bytes32) {
return set._values.get(account, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set, address account) private view returns (bytes32[] memory) {
return set._values.getAll(account);
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] // Magic value for ERC-1271 valid signature bytes4 constant ERC1271_MAGICVALUE = 0x1626ba7e; // Value indicating an invalid ERC-1271 signature bytes4 constant ERC1271_INVALID = 0xFFFFFFFF; // Value indicating successful validation uint256 constant VALIDATION_SUCCESS = 0; // Value indicating failed validation uint256 constant VALIDATION_FAILED = 1; // Module type identifier for Multitype install uint256 constant MODULE_TYPE_MULTI = 0; // Module type identifier for validators uint256 constant MODULE_TYPE_VALIDATOR = 1; // Module type identifier for executors uint256 constant MODULE_TYPE_EXECUTOR = 2; // Module type identifier for fallback handlers uint256 constant MODULE_TYPE_FALLBACK = 3; // Module type identifier for hooks uint256 constant MODULE_TYPE_HOOK = 4; string constant MODULE_ENABLE_MODE_NOTATION = "ModuleEnableMode(address module,uint256 moduleType,bytes32 userOpHash,bytes32 initDataHash)"; bytes32 constant MODULE_ENABLE_MODE_TYPE_HASH = keccak256(bytes(MODULE_ENABLE_MODE_NOTATION)); // Validation modes bytes1 constant MODE_VALIDATION = 0x00; bytes1 constant MODE_MODULE_ENABLE = 0x01; bytes4 constant SUPPORTS_ERC7739 = 0x77390000; bytes4 constant SUPPORTS_ERC7739_V1 = 0x77390001;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] /// @title Nexus - ERC-7579 Module Base Interface /// @notice Interface for module management in smart accounts, complying with ERC-7579 specifications. /// @dev Defines the lifecycle hooks and checks for modules within the smart account architecture. /// This interface includes methods for installing, uninstalling, and verifying module types and initialization status. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IModule { /// @notice Installs the module with necessary initialization data. /// @dev Reverts if the module is already initialized. /// @param data Arbitrary data required for initializing the module during `onInstall`. function onInstall(bytes calldata data) external; /// @notice Uninstalls the module and allows for cleanup via arbitrary data. /// @dev Reverts if any issues occur that prevent clean uninstallation. /// @param data Arbitrary data required for deinitializing the module during `onUninstall`. function onUninstall(bytes calldata data) external; /// @notice Determines if the module matches a specific module type. /// @dev Should return true if the module corresponds to the type ID, false otherwise. /// @param moduleTypeId Numeric ID of the module type as per ERC-7579 specifications. /// @return True if the module is of the specified type, false otherwise. function isModuleType(uint256 moduleTypeId) external view returns (bool); /// @notice Checks if the module has been initialized for a specific smart account. /// @dev Returns true if initialized, false otherwise. /// @param smartAccount Address of the smart account to check for initialization status. /// @return True if the module is initialized for the given smart account, false otherwise. function isInitialized(address smartAccount) external view returns (bool); }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
library AssociatedArrayLib {
using AssociatedArrayLib for *;
struct Array {
uint256 _spacer;
}
struct Bytes32Array {
Array _inner;
}
struct AddressArray {
Array _inner;
}
struct UintArray {
Array _inner;
}
error AssociatedArray_OutOfBounds(uint256 index);
function add(Bytes32Array storage s, address account, bytes32 value) internal {
if (!_contains(s._inner, account, value)) {
_push(s._inner, account, value);
}
}
function set(Bytes32Array storage s, address account, uint256 index, bytes32 value) internal {
_set(s._inner, account, index, value);
}
function push(Bytes32Array storage s, address account, bytes32 value) internal {
_push(s._inner, account, value);
}
function pop(Bytes32Array storage s, address account) internal {
_pop(s._inner, account);
}
function remove(Bytes32Array storage s, address account, uint256 index) internal {
_remove(s._inner, account, index);
}
function add(UintArray storage s, address account, uint256 value) internal {
if (!_contains(s._inner, account, bytes32(value))) {
_push(s._inner, account, bytes32(value));
}
}
function set(UintArray storage s, address account, uint256 index, uint256 value) internal {
_set(s._inner, account, index, bytes32(value));
}
function push(UintArray storage s, address account, uint256 value) internal {
_push(s._inner, account, bytes32(value));
}
function pop(UintArray storage s, address account) internal {
_pop(s._inner, account);
}
function remove(UintArray storage s, address account, uint256 index) internal {
_remove(s._inner, account, index);
}
function add(AddressArray storage s, address account, address value) internal {
if (!_contains(s._inner, account, bytes32(uint256(uint160(value))))) {
_push(s._inner, account, bytes32(uint256(uint160(value))));
}
}
function set(AddressArray storage s, address account, uint256 index, address value) internal {
_set(s._inner, account, index, bytes32(uint256(uint160(value))));
}
function push(AddressArray storage s, address account, address value) internal {
_push(s._inner, account, bytes32(uint256(uint160(value))));
}
function pop(AddressArray storage s, address account) internal {
_pop(s._inner, account);
}
function remove(AddressArray storage s, address account, uint256 index) internal {
_remove(s._inner, account, index);
}
function length(Bytes32Array storage s, address account) internal view returns (uint256) {
return _length(s._inner, account);
}
function get(Bytes32Array storage s, address account, uint256 index) internal view returns (bytes32) {
return _get(s._inner, account, index);
}
function getAll(Bytes32Array storage s, address account) internal view returns (bytes32[] memory) {
return _getAll(s._inner, account);
}
function contains(Bytes32Array storage s, address account, bytes32 value) internal view returns (bool) {
return _contains(s._inner, account, value);
}
function length(AddressArray storage s, address account) internal view returns (uint256) {
return _length(s._inner, account);
}
function get(AddressArray storage s, address account, uint256 index) internal view returns (address) {
return address(uint160(uint256(_get(s._inner, account, index))));
}
function getAll(AddressArray storage s, address account) internal view returns (address[] memory) {
bytes32[] memory bytes32Array = _getAll(s._inner, account);
address[] memory addressArray;
/// @solidity memory-safe-assembly
assembly {
addressArray := bytes32Array
}
return addressArray;
}
function contains(AddressArray storage s, address account, address value) internal view returns (bool) {
return _contains(s._inner, account, bytes32(uint256(uint160(value))));
}
function length(UintArray storage s, address account) internal view returns (uint256) {
return _length(s._inner, account);
}
function get(UintArray storage s, address account, uint256 index) internal view returns (uint256) {
return uint256(_get(s._inner, account, index));
}
function getAll(UintArray storage s, address account) internal view returns (uint256[] memory) {
bytes32[] memory bytes32Array = _getAll(s._inner, account);
uint256[] memory uintArray;
/// @solidity memory-safe-assembly
assembly {
uintArray := bytes32Array
}
return uintArray;
}
function contains(UintArray storage s, address account, uint256 value) internal view returns (bool) {
return _contains(s._inner, account, bytes32(value));
}
function _set(Array storage s, address account, uint256 index, bytes32 value) private {
_set(_slot(s, account), index, value);
}
function _set(bytes32 slot, uint256 index, bytes32 value) private {
assembly {
//if (index >= _length(s, account)) revert AssociatedArray_OutOfBounds(index);
if iszero(lt(index, sload(slot))) {
mstore(0, 0x8277484f) // `AssociatedArray_OutOfBounds(uint256)`
mstore(0x20, index)
revert(0x1c, 0x24)
}
sstore(add(slot, mul(0x20, add(index, 1))), value)
}
}
function _push(Array storage s, address account, bytes32 value) private {
bytes32 slot = _slot(s, account);
assembly {
// load length (stored @ slot), add 1 to it => index.
// mul index by 0x20 and add it to orig slot to get the next free slot
let index := add(sload(slot), 1)
sstore(add(slot, mul(0x20, index)), value)
sstore(slot, index) //increment length by 1
}
}
function _pop(Array storage s, address account) private {
bytes32 slot = _slot(s, account);
uint256 __length;
assembly {
__length := sload(slot)
}
if (__length == 0) return;
_set(slot, __length - 1, 0);
assembly {
sstore(slot, sub(__length, 1))
}
}
function _remove(Array storage s, address account, uint256 index) private {
bytes32 slot = _slot(s, account);
uint256 __length;
assembly {
__length := sload(slot)
if iszero(lt(index, __length)) {
mstore(0, 0x8277484f) // `AssociatedArray_OutOfBounds(uint256)`
mstore(0x20, index)
revert(0x1c, 0x24)
}
}
_set(slot, index, _get(s, account, __length - 1));
assembly {
// clear the last slot
// this is the 'unchecked' version of _set(slot, __length - 1, 0)
// as we use length-1 as index, so the check is excessive.
// also removes extra -1 and +1 operations
sstore(add(slot, mul(0x20, __length)), 0)
// store new length
sstore(slot, sub(__length, 1))
}
}
function _length(Array storage s, address account) private view returns (uint256 __length) {
bytes32 slot = _slot(s, account);
assembly {
__length := sload(slot)
}
}
function _get(Array storage s, address account, uint256 index) private view returns (bytes32 value) {
return _get(_slot(s, account), index);
}
function _get(bytes32 slot, uint256 index) private view returns (bytes32 value) {
assembly {
//if (index >= _length(s, account)) revert AssociatedArray_OutOfBounds(index);
if iszero(lt(index, sload(slot))) {
mstore(0, 0x8277484f) // `AssociatedArray_OutOfBounds(uint256)`
mstore(0x20, index)
revert(0x1c, 0x24)
}
value := sload(add(slot, mul(0x20, add(index, 1))))
}
}
function _getAll(Array storage s, address account) private view returns (bytes32[] memory values) {
bytes32 slot = _slot(s, account);
uint256 __length;
assembly {
__length := sload(slot)
}
values = new bytes32[](__length);
for (uint256 i; i < __length; i++) {
values[i] = _get(slot, i);
}
}
// inefficient. complexity = O(n)
// use with caution
// in case of large arrays, consider using EnumerableSet4337 instead
function _contains(Array storage s, address account, bytes32 value) private view returns (bool) {
bytes32 slot = _slot(s, account);
uint256 __length;
assembly {
__length := sload(slot)
}
for (uint256 i; i < __length; i++) {
if (_get(slot, i) == value) {
return true;
}
}
return false;
}
function _slot(Array storage s, address account) private pure returns (bytes32 __slot) {
assembly {
mstore(0x00, account)
mstore(0x20, s.slot)
__slot := keccak256(0x00, 0x40)
}
}
}{
"remappings": [
"@openzeppelin/=node_modules/@openzeppelin/",
"forge-std/=node_modules/forge-std/src/",
"account-abstraction/=node_modules/account-abstraction/contracts/",
"solady/=node_modules/solady/src/",
"excessively-safe-call/=node_modules/excessively-safe-call/src/",
"sentinellist/=node_modules/sentinellist/src/",
"solarray/=node_modules/solarray/src/",
"erc7739Validator/=node_modules/erc7739-validator-base/src/",
"@ERC4337/=node_modules/@ERC4337/",
"@gnosis.pm/=node_modules/@gnosis.pm/",
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@prb/=node_modules/@prb/",
"@prb/math/=node_modules/erc7739-validator-base/node_modules/@prb/math/src/",
"@rhinestone/=node_modules/@rhinestone/",
"@safe-global/=node_modules/@safe-global/",
"@zerodev/=node_modules/@zerodev/",
"ExcessivelySafeCall/=node_modules/erc7739-validator-base/node_modules/excessively-safe-call/src/",
"account-abstraction-v0.6/=node_modules/account-abstraction-v0.6/",
"ds-test/=node_modules/ds-test/",
"enumerablemap/=node_modules/enumerablemap/",
"enumerableset4337/=node_modules/erc7739-validator-base/node_modules/enumerablemap/src/",
"erc4337-validation/=node_modules/erc7739-validator-base/node_modules/@rhinestone/erc4337-validation/src/",
"erc7579/=node_modules/erc7579/",
"erc7739-validator-base/=node_modules/erc7739-validator-base/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"kernel/=node_modules/erc7739-validator-base/node_modules/@zerodev/kernel/src/",
"module-bases/=node_modules/erc7739-validator-base/node_modules/@rhinestone/module-bases/src/",
"modulekit/=node_modules/modulekit/",
"registry/=node_modules/modulekit/node_modules/@rhinestone/registry/src/",
"safe7579/=node_modules/erc7739-validator-base/node_modules/@rhinestone/safe7579/src/",
"stringutils/=node_modules/stringutils/"
],
"optimizer": {
"enabled": true,
"runs": 999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"ModuleAlreadyInitialized","type":"error"},{"inputs":[],"name":"NewOwnerIsContract","type":"error"},{"inputs":[],"name":"NoOwnerProvided","type":"error"},{"inputs":[],"name":"OwnerCannotBeZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"addSafeSender","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":"address","name":"sender","type":"address"},{"internalType":"address","name":"smartAccount","type":"address"}],"name":"isSafeSender","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignatureWithSender","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onInstall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"onUninstall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"removeSafeSender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"smartAccountOwners","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supportsNestedTypedDataSign","outputs":[{"internalType":"bytes32","name":"result","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"sig","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"validateSignatureWithData","outputs":[{"internalType":"bool","name":"validSig","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes32","name":"accountGasLimits","type":"bytes32"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"bytes32","name":"gasFees","type":"bytes32"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct PackedUserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]Contract Creation Code
6080604052348015600e575f5ffd5b506116958061001c5f395ff3fe608060405234801561000f575f5ffd5b50600436106100f0575f3560e01c8063940d384011610093578063e824b56811610063578063e824b56814610283578063ecd0596114610296578063f2fde38b146102aa578063f551e2ee146102bd575f5ffd5b8063940d3840146102165780639700320314610229578063d60b347f1461024a578063d620c85a1461025d575f5ffd5b806354fd4d50116100ce57806354fd4d50146101a25780635c81ca68146101db5780636d61fe70146101f05780638a91b0e314610203575f5ffd5b806306fdde03146100f45780630807dbc11461013f5780632e5b63a614610162575b5f5ffd5b60408051808201909152600b81527f4b3156616c696461746f7200000000000000000000000000000000000000000060208201525b60405161013691906110e0565b60405180910390f35b61015261014d36600461112c565b610301565b6040519015158152602001610136565b61018a610170366004611163565b5f602081905290815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610136565b60408051808201909152600581527f312e302e310000000000000000000000000000000000000000000000000000006020820152610129565b6101ee6101e9366004611163565b610317565b005b6101ee6101fe3660046111bc565b610327565b6101ee6102113660046111bc565b61047a565b6101526102243660046111fb565b6104af565b61023c610237366004611274565b61051d565b604051908152602001610136565b610152610258366004611163565b610573565b7fd620c85a0000000000000000000000000000000000000000000000000000000061023c565b6101ee610291366004611163565b610596565b6101526102a43660046112bb565b60011490565b6101ee6102b8366004611163565b6105a2565b6102d06102cb3660046112d2565b61063d565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610136565b5f61030e6001838561065d565b90505b92915050565b61032360013383610693565b5050565b5f819003610361576040517f1f2a381c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b335f908152602081905260409020546001600160a01b0316156103b0576040517fe72ce85e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6103be601482848661132a565b6103c791611351565b60601c905080610403576040517fc81abf6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b803b15610423576040516383e6a1cb60e01b815260040160405180910390fd5b335f908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038316179055601482111561047557610475610470836014818761132a565b6106a8565b505050565b335f818152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff191690556103239060019061070f565b5f601482146104ea576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6104f8601482858761132a565b61050191611351565b60601c905061051281888888610719565b979650505050505050565b5f61056081806105306020870187611163565b6001600160a01b03908116825260208201929092526040015f2054168361055b610100870187611391565b610719565b61056b57600161030e565b505f92915050565b6001600160a01b038082165f908152602081905260408120549091161515610311565b610323600133836107a7565b6001600160a01b0381166105e2576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b803b15610602576040516383e6a1cb60e01b815260040160405180910390fd5b335f908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b5f610652858561064d86866107bc565b6107ec565b90505b949350505050565b6001600160a01b038181165f908152600185016020908152604080832093861683529290529081205415155b90505b9392505050565b5f61068984846001600160a01b0385166108ce565b5f5b6106b56014836113e8565b811015610475576107063384846106cd856014611407565b906106d986600161141e565b6106e4906014611407565b926106f19392919061132a565b6106fa91611351565b6001919060601c610693565b506001016106aa565b6103238282610953565b5f846001600160a01b031661072f8585856109a7565b6001600160a01b03160361074557506001610655565b846001600160a01b0316610787610780866020527b19457468657265756d205369676e6564204d6573736167653a0a33325f52603c60042090565b85856109a7565b6001600160a01b03160361079d57506001610655565b505f949350505050565b5f61068984846001600160a01b0385166109e9565b818161649261ffff30801c190402818301601f190135036107e557506040810135016020810190355b9250929050565b5f816108295761773961ffff83190402840361082957507f7739000100000000000000000000000000000000000000000000000000000000610655565b60208301357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610888576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61089587878787610ac7565b806108a657506108a6868686610ae1565b806108b757506108b7868686610d88565b155f03631626ba7e1760e01b979650505050505050565b5f81815260018401602090815260408083206001600160a01b038616845290915281205461094c575f83815260208581526040909120805460010191820281018490555561091c8484610df3565b5f8381526001808701602090815260408084206001600160a01b038916855290915290912091909155905061068c565b505f61068c565b5f61095e8383610e06565b905060015b8181116109a1575f61097f858561097a8587611431565b610e11565b905061098c8585836109e9565b5050808061099990611444565b915050610963565b50505050565b5f61068983838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508893925050610e1d9050565b5f81815260018401602090815260408083206001600160a01b03861684529091528120548015610abe575f610a1f600183611431565b90505f6001610a2e8888610df3565b610a389190611431565b9050808214610a82575f610a4d888884610eb5565b9050610a5b88888584610ec1565b5f90815260018801602090815260408083206001600160a01b038a16845290915290208390555b610a8c8787610ecd565b5050505f8281526001808601602090815260408084206001600160a01b0388168552909152822091909155905061068c565b5f91505061068c565b5f610ad185610ed7565b1561065557610652848484610f17565b5f308015610b9a575f5f5f5f5f336001600160a01b03166384b0196e6040518163ffffffff1660e01b81526004015f60405180830381865afa158015610b29573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b50919081019061159d565b506040805186516020978801208183015285519590960194909420606086015260808501929092526001600160a01b031660a084015260c083015260e08201905296505050505050505b6040516002840385013560f01c8060420180860387016119015f52604081602037821582881017896042601e20181715610c05575f94507f983e65e5148e570cd828ead231ee759a8d7958721a768f93bc4483ba005c32de8552886020526040852098505050610d5e565b7f5479706564446174615369676e280000000000000000000000000000000000008452600e84018360408301823760288185019081525f1901515f1a602914610c7e575f6001858301035b6001820191506029828203515f1a1486831011610c5057508085039450808560408501018337602881830153505b6f07fffffe00000000000001000000000081515f1a1c5b602882515f1a14610cbb57806512010000000183515f1a1c179050600182019150610c95565b7f20636f6e74656e74732c737472696e67206e616d652c737472696e670000000082527f2076657273696f6e2c75696e7432353620636861696e49642c61646472657373601c8301527f20766572696679696e67436f6e74726163742c627974657333322073616c7429603c830152605c820191508460408401833760408388379084018590038520865260e08620604052600116604201601e20985050909403935b5060405280610d7457610d713386610f3b565b94505b610d7f858585610f17565b95945050505050565b5f3a61068c573a3a526d378edcd5b5b0a24f5342d8c1048560203a3a388461fffffa503a51610de757604051631626ba7e3a528160205260408052454561ffff0117805a108388141715610dd857fe5b3a3a6064601c3085fa50506040525b50610689848484610f17565b5f8181526020839052604081205461030e565b5f61030e8383610df3565b5f610689848484610eb5565b5f604051825160408114610e395760418114610e735750610eae565b604084015160ff81901c601b016020527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16606052610e86565b60608401515f1a60205260408401516060525b50835f5260208301516040526020604060805f60015afa505f6060523d606018519150806040525b5092915050565b5f610689848484611026565b6109a18484848461103d565b6103238282611055565b5f6dd9ecebf3c23529de49815dac1c4c6001600160a01b0383161480610f0557506001600160a01b03821633145b8061031157506103116001338461065d565b335f90815260208190526040812054610689906001600160a01b0316858585610719565b5f5f5f5f5f866001600160a01b03166384b0196e6040518163ffffffff1660e01b81526004015f60405180830381865afa158015610f7b573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610fa2919081019061159d565b50509450945094509450506040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815284516020860120602082015283516020850120604082015282606082015281608082015260a081209550506719010000000000005f5284601a5285603a52604260182094505f603a525050505092915050565b5f8281526020849052604081206106899083611091565b5f8381526020859052604090206109a19083836110b7565b5f818152602083905260408120805490918190036110735750505050565b61108882611082600184611431565b5f6110b7565b5f190190555050565b5f825482106110ab57638277484f5f52816020526024601cfd5b50600101602002015490565b825482106110d057638277484f5f52816020526024601cfd5b8060018301602002840155505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b6001600160a01b0381168114611129575f5ffd5b50565b5f5f6040838503121561113d575f5ffd5b823561114881611115565b9150602083013561115881611115565b809150509250929050565b5f60208284031215611173575f5ffd5b813561068c81611115565b5f5f83601f84011261118e575f5ffd5b50813567ffffffffffffffff8111156111a5575f5ffd5b6020830191508360208285010111156107e5575f5ffd5b5f5f602083850312156111cd575f5ffd5b823567ffffffffffffffff8111156111e3575f5ffd5b6111ef8582860161117e565b90969095509350505050565b5f5f5f5f5f6060868803121561120f575f5ffd5b85359450602086013567ffffffffffffffff81111561122c575f5ffd5b6112388882890161117e565b909550935050604086013567ffffffffffffffff811115611257575f5ffd5b6112638882890161117e565b969995985093965092949392505050565b5f5f60408385031215611285575f5ffd5b823567ffffffffffffffff81111561129b575f5ffd5b830161012081860312156112ad575f5ffd5b946020939093013593505050565b5f602082840312156112cb575f5ffd5b5035919050565b5f5f5f5f606085870312156112e5575f5ffd5b84356112f081611115565b935060208501359250604085013567ffffffffffffffff811115611312575f5ffd5b61131e8782880161117e565b95989497509550505050565b5f5f85851115611338575f5ffd5b83861115611344575f5ffd5b5050820193919092039150565b80356bffffffffffffffffffffffff198116906014841015610eae576bffffffffffffffffffffffff1960149490940360031b84901b1690921692915050565b5f5f8335601e198436030181126113a6575f5ffd5b83018035915067ffffffffffffffff8211156113c0575f5ffd5b6020019150368190038213156107e5575f5ffd5b634e487b7160e01b5f52601160045260245ffd5b5f8261140257634e487b7160e01b5f52601260045260245ffd5b500490565b8082028115828204841417610311576103116113d4565b80820180821115610311576103116113d4565b81810381811115610311576103116113d4565b5f60018201611455576114556113d4565b5060010190565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156114995761149961145c565b604052919050565b5f82601f8301126114b0575f5ffd5b815167ffffffffffffffff8111156114ca576114ca61145c565b6114dd601f8201601f1916602001611470565b8181528460208386010111156114f1575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b805161151881611115565b919050565b5f82601f83011261152c575f5ffd5b815167ffffffffffffffff8111156115465761154661145c565b8060051b61155660208201611470565b91825260208185018101929081019086841115611571575f5ffd5b6020860192505b83831015611593578251825260209283019290910190611578565b9695505050505050565b5f5f5f5f5f5f5f60e0888a0312156115b3575f5ffd5b87517fff00000000000000000000000000000000000000000000000000000000000000811681146115e2575f5ffd5b602089015190975067ffffffffffffffff8111156115fe575f5ffd5b61160a8a828b016114a1565b965050604088015167ffffffffffffffff811115611626575f5ffd5b6116328a828b016114a1565b60608a0151909650945061164a90506080890161150d565b60a089015160c08a0151919450925067ffffffffffffffff81111561166d575f5ffd5b6116798a828b0161151d565b9150509295989194975092955056fea164736f6c634300081b000a
Deployed Bytecode
0x608060405234801561000f575f5ffd5b50600436106100f0575f3560e01c8063940d384011610093578063e824b56811610063578063e824b56814610283578063ecd0596114610296578063f2fde38b146102aa578063f551e2ee146102bd575f5ffd5b8063940d3840146102165780639700320314610229578063d60b347f1461024a578063d620c85a1461025d575f5ffd5b806354fd4d50116100ce57806354fd4d50146101a25780635c81ca68146101db5780636d61fe70146101f05780638a91b0e314610203575f5ffd5b806306fdde03146100f45780630807dbc11461013f5780632e5b63a614610162575b5f5ffd5b60408051808201909152600b81527f4b3156616c696461746f7200000000000000000000000000000000000000000060208201525b60405161013691906110e0565b60405180910390f35b61015261014d36600461112c565b610301565b6040519015158152602001610136565b61018a610170366004611163565b5f602081905290815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610136565b60408051808201909152600581527f312e302e310000000000000000000000000000000000000000000000000000006020820152610129565b6101ee6101e9366004611163565b610317565b005b6101ee6101fe3660046111bc565b610327565b6101ee6102113660046111bc565b61047a565b6101526102243660046111fb565b6104af565b61023c610237366004611274565b61051d565b604051908152602001610136565b610152610258366004611163565b610573565b7fd620c85a0000000000000000000000000000000000000000000000000000000061023c565b6101ee610291366004611163565b610596565b6101526102a43660046112bb565b60011490565b6101ee6102b8366004611163565b6105a2565b6102d06102cb3660046112d2565b61063d565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610136565b5f61030e6001838561065d565b90505b92915050565b61032360013383610693565b5050565b5f819003610361576040517f1f2a381c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b335f908152602081905260409020546001600160a01b0316156103b0576040517fe72ce85e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6103be601482848661132a565b6103c791611351565b60601c905080610403576040517fc81abf6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b803b15610423576040516383e6a1cb60e01b815260040160405180910390fd5b335f908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038316179055601482111561047557610475610470836014818761132a565b6106a8565b505050565b335f818152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff191690556103239060019061070f565b5f601482146104ea576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6104f8601482858761132a565b61050191611351565b60601c905061051281888888610719565b979650505050505050565b5f61056081806105306020870187611163565b6001600160a01b03908116825260208201929092526040015f2054168361055b610100870187611391565b610719565b61056b57600161030e565b505f92915050565b6001600160a01b038082165f908152602081905260408120549091161515610311565b610323600133836107a7565b6001600160a01b0381166105e2576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b803b15610602576040516383e6a1cb60e01b815260040160405180910390fd5b335f908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b5f610652858561064d86866107bc565b6107ec565b90505b949350505050565b6001600160a01b038181165f908152600185016020908152604080832093861683529290529081205415155b90505b9392505050565b5f61068984846001600160a01b0385166108ce565b5f5b6106b56014836113e8565b811015610475576107063384846106cd856014611407565b906106d986600161141e565b6106e4906014611407565b926106f19392919061132a565b6106fa91611351565b6001919060601c610693565b506001016106aa565b6103238282610953565b5f846001600160a01b031661072f8585856109a7565b6001600160a01b03160361074557506001610655565b846001600160a01b0316610787610780866020527b19457468657265756d205369676e6564204d6573736167653a0a33325f52603c60042090565b85856109a7565b6001600160a01b03160361079d57506001610655565b505f949350505050565b5f61068984846001600160a01b0385166109e9565b818161649261ffff30801c190402818301601f190135036107e557506040810135016020810190355b9250929050565b5f816108295761773961ffff83190402840361082957507f7739000100000000000000000000000000000000000000000000000000000000610655565b60208301357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610888576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61089587878787610ac7565b806108a657506108a6868686610ae1565b806108b757506108b7868686610d88565b155f03631626ba7e1760e01b979650505050505050565b5f81815260018401602090815260408083206001600160a01b038616845290915281205461094c575f83815260208581526040909120805460010191820281018490555561091c8484610df3565b5f8381526001808701602090815260408084206001600160a01b038916855290915290912091909155905061068c565b505f61068c565b5f61095e8383610e06565b905060015b8181116109a1575f61097f858561097a8587611431565b610e11565b905061098c8585836109e9565b5050808061099990611444565b915050610963565b50505050565b5f61068983838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508893925050610e1d9050565b5f81815260018401602090815260408083206001600160a01b03861684529091528120548015610abe575f610a1f600183611431565b90505f6001610a2e8888610df3565b610a389190611431565b9050808214610a82575f610a4d888884610eb5565b9050610a5b88888584610ec1565b5f90815260018801602090815260408083206001600160a01b038a16845290915290208390555b610a8c8787610ecd565b5050505f8281526001808601602090815260408084206001600160a01b0388168552909152822091909155905061068c565b5f91505061068c565b5f610ad185610ed7565b1561065557610652848484610f17565b5f308015610b9a575f5f5f5f5f336001600160a01b03166384b0196e6040518163ffffffff1660e01b81526004015f60405180830381865afa158015610b29573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b50919081019061159d565b506040805186516020978801208183015285519590960194909420606086015260808501929092526001600160a01b031660a084015260c083015260e08201905296505050505050505b6040516002840385013560f01c8060420180860387016119015f52604081602037821582881017896042601e20181715610c05575f94507f983e65e5148e570cd828ead231ee759a8d7958721a768f93bc4483ba005c32de8552886020526040852098505050610d5e565b7f5479706564446174615369676e280000000000000000000000000000000000008452600e84018360408301823760288185019081525f1901515f1a602914610c7e575f6001858301035b6001820191506029828203515f1a1486831011610c5057508085039450808560408501018337602881830153505b6f07fffffe00000000000001000000000081515f1a1c5b602882515f1a14610cbb57806512010000000183515f1a1c179050600182019150610c95565b7f20636f6e74656e74732c737472696e67206e616d652c737472696e670000000082527f2076657273696f6e2c75696e7432353620636861696e49642c61646472657373601c8301527f20766572696679696e67436f6e74726163742c627974657333322073616c7429603c830152605c820191508460408401833760408388379084018590038520865260e08620604052600116604201601e20985050909403935b5060405280610d7457610d713386610f3b565b94505b610d7f858585610f17565b95945050505050565b5f3a61068c573a3a526d378edcd5b5b0a24f5342d8c1048560203a3a388461fffffa503a51610de757604051631626ba7e3a528160205260408052454561ffff0117805a108388141715610dd857fe5b3a3a6064601c3085fa50506040525b50610689848484610f17565b5f8181526020839052604081205461030e565b5f61030e8383610df3565b5f610689848484610eb5565b5f604051825160408114610e395760418114610e735750610eae565b604084015160ff81901c601b016020527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16606052610e86565b60608401515f1a60205260408401516060525b50835f5260208301516040526020604060805f60015afa505f6060523d606018519150806040525b5092915050565b5f610689848484611026565b6109a18484848461103d565b6103238282611055565b5f6dd9ecebf3c23529de49815dac1c4c6001600160a01b0383161480610f0557506001600160a01b03821633145b8061031157506103116001338461065d565b335f90815260208190526040812054610689906001600160a01b0316858585610719565b5f5f5f5f5f866001600160a01b03166384b0196e6040518163ffffffff1660e01b81526004015f60405180830381865afa158015610f7b573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610fa2919081019061159d565b50509450945094509450506040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815284516020860120602082015283516020850120604082015282606082015281608082015260a081209550506719010000000000005f5284601a5285603a52604260182094505f603a525050505092915050565b5f8281526020849052604081206106899083611091565b5f8381526020859052604090206109a19083836110b7565b5f818152602083905260408120805490918190036110735750505050565b61108882611082600184611431565b5f6110b7565b5f190190555050565b5f825482106110ab57638277484f5f52816020526024601cfd5b50600101602002015490565b825482106110d057638277484f5f52816020526024601cfd5b8060018301602002840155505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b6001600160a01b0381168114611129575f5ffd5b50565b5f5f6040838503121561113d575f5ffd5b823561114881611115565b9150602083013561115881611115565b809150509250929050565b5f60208284031215611173575f5ffd5b813561068c81611115565b5f5f83601f84011261118e575f5ffd5b50813567ffffffffffffffff8111156111a5575f5ffd5b6020830191508360208285010111156107e5575f5ffd5b5f5f602083850312156111cd575f5ffd5b823567ffffffffffffffff8111156111e3575f5ffd5b6111ef8582860161117e565b90969095509350505050565b5f5f5f5f5f6060868803121561120f575f5ffd5b85359450602086013567ffffffffffffffff81111561122c575f5ffd5b6112388882890161117e565b909550935050604086013567ffffffffffffffff811115611257575f5ffd5b6112638882890161117e565b969995985093965092949392505050565b5f5f60408385031215611285575f5ffd5b823567ffffffffffffffff81111561129b575f5ffd5b830161012081860312156112ad575f5ffd5b946020939093013593505050565b5f602082840312156112cb575f5ffd5b5035919050565b5f5f5f5f606085870312156112e5575f5ffd5b84356112f081611115565b935060208501359250604085013567ffffffffffffffff811115611312575f5ffd5b61131e8782880161117e565b95989497509550505050565b5f5f85851115611338575f5ffd5b83861115611344575f5ffd5b5050820193919092039150565b80356bffffffffffffffffffffffff198116906014841015610eae576bffffffffffffffffffffffff1960149490940360031b84901b1690921692915050565b5f5f8335601e198436030181126113a6575f5ffd5b83018035915067ffffffffffffffff8211156113c0575f5ffd5b6020019150368190038213156107e5575f5ffd5b634e487b7160e01b5f52601160045260245ffd5b5f8261140257634e487b7160e01b5f52601260045260245ffd5b500490565b8082028115828204841417610311576103116113d4565b80820180821115610311576103116113d4565b81810381811115610311576103116113d4565b5f60018201611455576114556113d4565b5060010190565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156114995761149961145c565b604052919050565b5f82601f8301126114b0575f5ffd5b815167ffffffffffffffff8111156114ca576114ca61145c565b6114dd601f8201601f1916602001611470565b8181528460208386010111156114f1575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b805161151881611115565b919050565b5f82601f83011261152c575f5ffd5b815167ffffffffffffffff8111156115465761154661145c565b8060051b61155660208201611470565b91825260208185018101929081019086841115611571575f5ffd5b6020860192505b83831015611593578251825260209283019290910190611578565b9695505050505050565b5f5f5f5f5f5f5f60e0888a0312156115b3575f5ffd5b87517fff00000000000000000000000000000000000000000000000000000000000000811681146115e2575f5ffd5b602089015190975067ffffffffffffffff8111156115fe575f5ffd5b61160a8a828b016114a1565b965050604088015167ffffffffffffffff811115611626575f5ffd5b6116328a828b016114a1565b60608a0151909650945061164a90506080890161150d565b60a089015160c08a0151919450925067ffffffffffffffff81111561166d575f5ffd5b6116798a828b0161151d565b9150509295989194975092955056fea164736f6c634300081b000a
Loading...
Loading
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 ]
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.