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 | |||
---|---|---|---|---|---|---|
6562384 | 42 hrs ago | Contract Creation | 0 S |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x2D5bECCd...c59b2659e The constructor portion of the code might be different and could alter the actual behaviour of the contract
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"}]
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.