Source Code
Overview
S Balance
0 S
More Info
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
6348609 | 2 days ago | Contract Creation | 0 S |
Loading...
Loading
Contract Source Code Verified (Exact Match)
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": {} }
[{"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
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ 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.