Overview
S Balance
0 S
S Value
-More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
1455078 | 3 days ago | Contract Creation | 0 S |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
K1MeeValidator
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; import {IValidator, MODULE_TYPE_VALIDATOR} from "../interfaces/IERC7579Module.sol"; import {ERC7739Validator} from "erc7739Validator/ERC7739Validator.sol"; // Fusion libraries - validate userOp using on-chain tx or off-chain permit import {EnumerableSet} from "../libraries/storage/EnumerableSet4337.sol"; import "../libraries/SuperTxEcdsaValidatorLib.sol"; contract K1MeeValidator is IValidator, ERC7739Validator { // using SignatureCheckerLib for address; 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 override returns (uint256) { address owner = smartAccountOwners[userOp.sender]; return SuperTxEcdsaValidatorLib.validateUserOp(userOp, userOpHash, owner); } /** * Validates an ERC-1271 signature * * @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 */ function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata signature) external view virtual override returns (bytes4 sigValidationResult) { 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 pure 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 "K1MeeValidator"; } /// @notice Returns the version of the module /// @return The version of the module function version() external pure returns (string memory) { return "1.0.0"; } /// @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 //////////////////////////////////////////////////////////////////////////*/ /// @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 pure returns (bool) { return SuperTxEcdsaValidatorLib.validateSignatureForOwner(owner, hash, signature); } // @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.27; import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol"; uint256 constant VALIDATION_SUCCESS = 0; uint256 constant VALIDATION_FAILED = 1; uint256 constant MODULE_TYPE_VALIDATOR = 1; uint256 constant MODULE_TYPE_EXECUTOR = 2; uint256 constant MODULE_TYPE_FALLBACK = 3; uint256 constant MODULE_TYPE_HOOK = 4; interface IModule { error AlreadyInitialized(address smartAccount); error NotInitialized(address smartAccount); /** * @dev This function is called by the smart account during installation of the module * @param data arbitrary data that may be required on the module during `onInstall` * initialization * * MUST revert on error (i.e. if module is already enabled) */ function onInstall(bytes calldata data) external; /** * @dev This function is called by the smart account during uninstallation of the module * @param data arbitrary data that may be required on the module during `onUninstall` * de-initialization * * MUST revert on error */ function onUninstall(bytes calldata data) external; /** * @dev Returns boolean value if module is a certain type * @param moduleTypeId the module type ID according the ERC-7579 spec * * MUST return true if the module is of the given type and false otherwise */ function isModuleType(uint256 moduleTypeId) external view returns (bool); /** * @dev Returns if the module was already initialized for a provided smartaccount */ function isInitialized(address smartAccount) external view returns (bool); } interface IValidator is IModule { error InvalidTargetAddress(address target); /** * @dev Validates a transaction on behalf of the account. * This function is intended to be called by the MSA during the ERC-4337 validaton phase * Note: solely relying on bytes32 hash and signature is not suffcient for some * validation implementations (i.e. SessionKeys often need access to userOp.calldata) * @param userOp The user operation to be validated. The userOp MUST NOT contain any metadata. * The MSA MUST clean up the userOp before sending it to the validator. * @param userOpHash The hash of the user operation to be validated * @return return value according to ERC-4337 */ function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); /** * Validator can be used for ERC-1271 validation */ function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data) external view returns (bytes4); } interface IExecutor is IModule {} interface IHook is IModule { function preCheck(address msgSender, uint256 msgValue, bytes calldata msgData) external returns (bytes memory hookData); function postCheck(bytes calldata hookData) external; } interface IFallback is IModule {}
// 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_V1 = 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_V1; } } // 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; 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: AGPL-3.0-only pragma solidity ^0.8.27; import "account-abstraction/interfaces/PackedUserOperation.sol"; import "./fusion/PermitValidatorLib.sol"; import "./fusion/TxValidatorLib.sol"; import "./fusion/EcdsaValidatorLib.sol"; import "./fusion/UserOpValidatorLib.sol"; import "./util/BytesLib.sol"; library SuperTxEcdsaValidatorLib { using BytesLib for bytes; enum SuperSignatureType { OFF_CHAIN, ON_CHAIN, ERC20_PERMIT, USEROP } uint8 constant SIG_TYPE_OFF_CHAIN = 0x00; uint8 constant SIG_TYPE_ON_CHAIN = 0x01; uint8 constant SIG_TYPE_ERC20_PERMIT = 0x02; // ...leave space for other sig types: ERC-7683, Permit2, etc uint8 constant SIG_TYPE_USEROP = 0xff; struct SuperSignature { SuperSignatureType signatureType; bytes signature; } function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, address owner) internal returns (uint256) { SuperSignature memory decodedSig = decodeSignature(userOp.signature); if (decodedSig.signatureType == SuperSignatureType.OFF_CHAIN) { return EcdsaValidatorLib.validateUserOp(userOp, decodedSig.signature, owner); } else if (decodedSig.signatureType == SuperSignatureType.ON_CHAIN) { return TxValidatorLib.validateUserOp(userOp, decodedSig.signature, owner); } else if (decodedSig.signatureType == SuperSignatureType.ERC20_PERMIT) { return PermitValidatorLib.validateUserOp(userOp, decodedSig.signature, owner); } else if (decodedSig.signatureType == SuperSignatureType.USEROP) { return UserOpValidatorLib.validateUserOp(userOpHash, decodedSig.signature, owner); } else { revert("SuperTxEcdsaValidatorLib:: invalid userOp sig type"); } } function validateSignatureForOwner(address owner, bytes32 hash, bytes memory signature) internal pure returns (bool) { SuperSignature memory decodedSig = decodeSignature(signature); if (decodedSig.signatureType == SuperSignatureType.OFF_CHAIN) { return EcdsaValidatorLib.validateSignatureForOwner(owner, hash, decodedSig.signature); } else if (decodedSig.signatureType == SuperSignatureType.ON_CHAIN) { return TxValidatorLib.validateSignatureForOwner(owner, hash, decodedSig.signature); } else if (decodedSig.signatureType == SuperSignatureType.ERC20_PERMIT) { return PermitValidatorLib.validateSignatureForOwner(owner, hash, decodedSig.signature); } else if (decodedSig.signatureType == SuperSignatureType.USEROP) { return UserOpValidatorLib.validateSignatureForOwner(owner, hash, decodedSig.signature); } else { revert("SuperTxEcdsaValidatorLib:: invalid userOp sig type"); } } function decodeSignature(bytes memory self) internal pure returns (SuperSignature memory) { bytes memory sig = self.slice(1, self.length - 1); if (uint8(self[0]) == SIG_TYPE_OFF_CHAIN) { return SuperSignature(SuperSignatureType.OFF_CHAIN, sig); } else if (uint8(self[0]) == SIG_TYPE_ON_CHAIN) { return SuperSignature(SuperSignatureType.ON_CHAIN, sig); } else if (uint8(self[0]) == SIG_TYPE_ERC20_PERMIT) { return SuperSignature(SuperSignatureType.ERC20_PERMIT, sig); } else if (uint8(self[0]) == SIG_TYPE_USEROP) { return SuperSignature(SuperSignatureType.USEROP, sig); } else { revert( "SuperTxEcdsaValidatorLib:: invalid sig type. Expected prefix 0x00 for off-chain, 0x01 for on-chain or 0x02 for erc20 permit itx hash signature or 0xff for normal userOp signature." ); } } }
// 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; 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) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import "openzeppelin/utils/cryptography/MerkleProof.sol"; import "account-abstraction/interfaces/PackedUserOperation.sol"; import "account-abstraction/core/Helpers.sol"; import "../../interfaces/IERC20Permit.sol"; import "../util/EcdsaLib.sol"; import "../util/UserOpLib.sol"; library PermitValidatorLib { uint8 constant EIP_155_MIN_V_VALUE = 37; using MessageHashUtils for bytes32; struct DecodedErc20PermitSig { IERC20Permit token; address spender; bytes32 permitTypehash; bytes32 domainSeparator; uint256 amount; uint256 chainId; uint256 nonce; bool isPermitTx; bytes32 appendedHash; bytes32[] proof; uint48 lowerBoundTimestamp; uint48 upperBoundTimestamp; uint256 v; bytes32 r; bytes32 s; } /** * This function parses the given userOpSignature into a DecodedErc20PermitSig data structure. * * Once parsed, the function will check for two conditions: * 1. is the expected hash found in the signed Permit message's deadline field? * 2. is the recovered message signer equal to the expected signer? * * If both conditions are met - outside contract can be sure that the expected signer has indeed * approved the given hash by signing a given Permit message. * * NOTES: This function will revert if either of following is met: * 1. the userOpSignature couldn't be abi.decoded into a valid DecodedErc20PermitSig struct as defined in this contract * 2. extracted hash wasn't equal to the provided expected hash * 3. recovered Permit message signer wasn't equal to the expected signer * * Returns true if the expected signer did indeed approve the given expectedHash by signing an on-chain transaction. * In that case, the function will also perform the Permit approval on the given token in case the * isPermitTx flag was set to true in the decoded signature struct. * * @param userOp UserOp being validated. * @param parsedSignature Signature provided as the userOp.signature parameter (minus the prepended tx type byte). * @param expectedSigner Signer expected to be recovered when decoding the ERC20OPermit signature. */ function validateUserOp(PackedUserOperation calldata userOp, bytes memory parsedSignature, address expectedSigner) internal returns (uint256) { DecodedErc20PermitSig memory decodedSig = abi.decode(parsedSignature, (DecodedErc20PermitSig)); bytes32 userOpHash = UserOpLib.getUserOpHash(userOp, decodedSig.lowerBoundTimestamp, decodedSig.upperBoundTimestamp); uint8 vAdjusted = _adjustV(decodedSig.v); uint256 deadline = uint256(decodedSig.appendedHash); bytes32 structHash = keccak256( abi.encode( decodedSig.permitTypehash, expectedSigner, decodedSig.spender, decodedSig.amount, decodedSig.nonce, deadline ) ); bytes32 signedDataHash = _hashTypedDataV4(structHash, decodedSig.domainSeparator); bytes memory signature = abi.encodePacked(decodedSig.r, decodedSig.s, vAdjusted); if (!EcdsaLib.isValidSignature(expectedSigner, signedDataHash, signature)) { return SIG_VALIDATION_FAILED; } if (!MerkleProof.verify(decodedSig.proof, decodedSig.appendedHash, userOpHash)) { return SIG_VALIDATION_FAILED; } if (decodedSig.isPermitTx) { decodedSig.token.permit( expectedSigner, userOp.sender, decodedSig.amount, deadline, vAdjusted, decodedSig.r, decodedSig.s ); } return _packValidationData(false, decodedSig.upperBoundTimestamp, decodedSig.lowerBoundTimestamp); } function validateSignatureForOwner(address expectedSigner, bytes32 hash, bytes memory parsedSignature) internal pure returns (bool) { DecodedErc20PermitSig memory decodedSig = abi.decode(parsedSignature, (DecodedErc20PermitSig)); uint8 vAdjusted = _adjustV(decodedSig.v); uint256 deadline = uint256(decodedSig.appendedHash); bytes32 structHash = keccak256( abi.encode( decodedSig.permitTypehash, expectedSigner, decodedSig.spender, decodedSig.amount, decodedSig.nonce, deadline ) ); bytes32 signedDataHash = _hashTypedDataV4(structHash, decodedSig.domainSeparator); bytes memory signature = abi.encodePacked(decodedSig.r, decodedSig.s, vAdjusted); if (!EcdsaLib.isValidSignature(expectedSigner, signedDataHash, signature)) { return false; } if (!MerkleProof.verify(decodedSig.proof, decodedSig.appendedHash, hash)) { return false; } return true; } function _hashTypedDataV4(bytes32 structHash, bytes32 domainSeparator) private pure returns (bytes32) { return MessageHashUtils.toTypedDataHash(domainSeparator, structHash); } function _adjustV(uint256 v) private pure returns (uint8) { if (v >= EIP_155_MIN_V_VALUE) { return uint8((v - 2 * _extractChainIdFromV(v) - 35) + 27); } else if (v <= 1) { return uint8(v + 27); } else { return uint8(v); } } function _extractChainIdFromV(uint256 v) private pure returns (uint256 chainId) { chainId = (v - 35) / 2; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import "openzeppelin/utils/cryptography/MerkleProof.sol"; import "account-abstraction/interfaces/PackedUserOperation.sol"; import "account-abstraction/core/Helpers.sol"; import "../rlp/RLPDecoder.sol"; import "../rlp/RLPEncoder.sol"; import "../util/BytesLib.sol"; import "../util/UserOpLib.sol"; import "../util/EcdsaLib.sol"; library TxValidatorLib { uint8 constant LEGACY_TX_TYPE = 0x00; uint8 constant EIP1559_TX_TYPE = 0x02; uint8 constant RLP_ENCODED_R_S_BYTE_SIZE = 66; // 2 * 33bytes (for r, s components) uint8 constant EIP_155_MIN_V_VALUE = 37; uint8 constant HASH_BYTE_SIZE = 32; uint8 constant TIMESTAMP_BYTE_SIZE = 6; uint8 constant PROOF_ITEM_BYTE_SIZE = 32; uint8 constant ITX_HASH_BYTE_SIZE = 32; using RLPDecoder for RLPDecoder.RLPItem; using RLPDecoder for bytes; using RLPEncoder for uint256; using BytesLib for bytes; struct TxData { uint8 txType; uint8 v; bytes32 r; bytes32 s; bytes32 utxHash; bytes32 superTxHash; bytes32[] proof; uint48 lowerBoundTimestamp; uint48 upperBoundTimestamp; } struct TxParams { uint256 v; bytes32 r; bytes32 s; bytes callData; } /** * This function parses the given userOpSignature into a valid fully signed EVM transaction. * Once parsed, the function will check for three conditions: * 1. is the expected hash found in the tx.data as the last 32bytes? * 2. is the recovered tx signer equal to the expected signer? * 2. is the given UserOp a part of the merkle tree * * If all the conditions are met - outside contract can be sure that the expected signer has indeed * approved the given hash by performing given on-chain transaction. * * NOTES: This function will revert if either of following is met: * 1. the userOpSignature couldn't be parsed to a valid fully signed EVM transaction * 2. hash couldn't be extracted from the tx.data * 3. extracted hash wasn't equal to the provided expected hash * 4. recovered signer wasn't equal to the expected signer * * Returns true if the expected signer did indeed approve the given expectedHash by signing an on-chain transaction. * * @param userOp UserOp being validated. * @param parsedSignature Signature provided as the userOp.signature parameter (minus the prepended tx type byte). * Expecting to receive fully signed serialized EVM transcaction here of type 0x00 (LEGACY) * or 0x02 (EIP1556). * For LEGACY tx type the "0x00" prefix has to be added manually while the EIP1559 tx type * already contains 0x02 prefix. * @param expectedSigner Expected EOA signer of the given userOp and the EVM transaction. */ function validateUserOp(PackedUserOperation calldata userOp, bytes memory parsedSignature, address expectedSigner) internal view returns (uint256) { TxData memory decodedTx = decodeTx(parsedSignature); bytes32 userOpHash = UserOpLib.getUserOpHash(userOp, decodedTx.lowerBoundTimestamp, decodedTx.upperBoundTimestamp); bytes memory signature = abi.encodePacked(decodedTx.r, decodedTx.s, decodedTx.v); if (!EcdsaLib.isValidSignature(expectedSigner, decodedTx.utxHash, signature)) { return SIG_VALIDATION_FAILED; } if (!MerkleProof.verify(decodedTx.proof, decodedTx.superTxHash, userOpHash)) { return SIG_VALIDATION_FAILED; } return _packValidationData(false, decodedTx.upperBoundTimestamp, decodedTx.lowerBoundTimestamp); } function validateSignatureForOwner(address expectedSigner, bytes32 hash, bytes memory parsedSignature) internal pure returns (bool) { TxData memory decodedTx = decodeTx(parsedSignature); bytes memory signature = abi.encodePacked(decodedTx.r, decodedTx.s, decodedTx.v); if (!EcdsaLib.isValidSignature(expectedSigner, decodedTx.utxHash, signature)) { return false; } if (!MerkleProof.verify(decodedTx.proof, decodedTx.superTxHash, hash)) { return false; } return true; } function decodeTx(bytes memory self) internal pure returns (TxData memory) { uint8 txType = uint8(self[0]); //first byte is tx type uint48 lowerBoundTimestamp = uint48(bytes6((self.slice(self.length - 2 * TIMESTAMP_BYTE_SIZE, TIMESTAMP_BYTE_SIZE)))); uint48 upperBoundTimestamp = uint48(bytes6(self.slice(self.length - TIMESTAMP_BYTE_SIZE, TIMESTAMP_BYTE_SIZE))); uint8 proofItemsCount = uint8(self[self.length - 2 * TIMESTAMP_BYTE_SIZE - 1]); uint256 appendedDataLen = (uint256(proofItemsCount) * PROOF_ITEM_BYTE_SIZE + 1) + 2 * TIMESTAMP_BYTE_SIZE; bytes memory rlpEncodedTx = self.slice(1, self.length - appendedDataLen - 1); RLPDecoder.RLPItem memory parsedRlpEncodedTx = rlpEncodedTx.toRlpItem(); RLPDecoder.RLPItem[] memory parsedRlpEncodedTxItems = parsedRlpEncodedTx.toList(); TxParams memory params = extractParams(txType, parsedRlpEncodedTxItems); return TxData( txType, _adjustV(params.v), params.r, params.s, calculateUnsignedTxHash(txType, rlpEncodedTx, parsedRlpEncodedTx.payloadLen(), params.v), extractAppendedHash(params.callData), extractProof(self, proofItemsCount), lowerBoundTimestamp, upperBoundTimestamp ); } function extractParams(uint8 txType, RLPDecoder.RLPItem[] memory items) private pure returns (TxParams memory params) { uint8 dataPos; uint8 vPos; uint8 rPos; uint8 sPos; if (txType == LEGACY_TX_TYPE) { dataPos = 5; vPos = 6; rPos = 7; sPos = 8; } else if (txType == EIP1559_TX_TYPE) { dataPos = 7; vPos = 9; rPos = 10; sPos = 11; } else { revert("TxValidatorLib:: unsupported evm tx type"); } return TxParams( items[vPos].toUint(), bytes32(items[rPos].toUint()), bytes32(items[sPos].toUint()), items[dataPos].toBytes() ); } function extractAppendedHash(bytes memory callData) private pure returns (bytes32 iTxHash) { if (callData.length < ITX_HASH_BYTE_SIZE) revert("TxDecoder:: callData length too short"); iTxHash = bytes32(callData.slice(callData.length - ITX_HASH_BYTE_SIZE, ITX_HASH_BYTE_SIZE)); } function extractProof(bytes memory signedTx, uint8 proofItemsCount) private pure returns (bytes32[] memory proof) { proof = new bytes32[](proofItemsCount); uint256 pos = signedTx.length - 2 * TIMESTAMP_BYTE_SIZE - 1; for (proofItemsCount; proofItemsCount > 0; proofItemsCount--) { proof[proofItemsCount - 1] = bytes32(signedTx.slice(pos - PROOF_ITEM_BYTE_SIZE, PROOF_ITEM_BYTE_SIZE)); } } function calculateUnsignedTxHash(uint8 txType, bytes memory rlpEncodedTx, uint256 rlpEncodedTxPayloadLen, uint256 v) private pure returns (bytes32 hash) { uint256 totalSignatureSize = RLP_ENCODED_R_S_BYTE_SIZE + v.encodeUint().length; uint256 totalPrefixSize = rlpEncodedTx.length - rlpEncodedTxPayloadLen; bytes memory rlpEncodedTxNoSigAndPrefix = rlpEncodedTx.slice(totalPrefixSize, rlpEncodedTx.length - totalSignatureSize - totalPrefixSize); if (txType == EIP1559_TX_TYPE) { return keccak256(abi.encodePacked(txType, prependRlpContentSize(rlpEncodedTxNoSigAndPrefix, ""))); } else if (txType == LEGACY_TX_TYPE) { if (v >= EIP_155_MIN_V_VALUE) { return keccak256( prependRlpContentSize( rlpEncodedTxNoSigAndPrefix, abi.encodePacked( uint256(_extractChainIdFromV(v)).encodeUint(), uint256(0).encodeUint(), uint256(0).encodeUint() ) ) ); } else { return keccak256(prependRlpContentSize(rlpEncodedTxNoSigAndPrefix, "")); } } else { revert("TxValidatorLib:: unsupported tx type"); } } function prependRlpContentSize(bytes memory content, bytes memory extraData) public pure returns (bytes memory) { bytes memory combinedContent = abi.encodePacked(content, extraData); return abi.encodePacked(combinedContent.length.encodeLength(RLPDecoder.LIST_SHORT_START), combinedContent); } function _adjustV(uint256 v) internal pure returns (uint8) { if (v >= EIP_155_MIN_V_VALUE) { return uint8((v - 2 * _extractChainIdFromV(v) - 35) + 27); } else if (v <= 1) { return uint8(v + 27); } else { return uint8(v); } } function _extractChainIdFromV(uint256 v) internal pure returns (uint256 chainId) { chainId = (v - 35) / 2; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import "openzeppelin/utils/cryptography/MerkleProof.sol"; import "account-abstraction/interfaces/PackedUserOperation.sol"; import "account-abstraction/core/Helpers.sol"; import "../util/EcdsaLib.sol"; import "../util/UserOpLib.sol"; library EcdsaValidatorLib { /** * This function parses the given userOpSignature into a Supertransaction signature * * Once parsed, the function will check for two conditions: * 1. is the root supertransaction hash signed by the account owner's EOA * 2. is the userOp actually a part of the given supertransaction * * If both conditions are met - outside contract can be sure that the expected signer has indeed * approved the given userOp - and the userOp is successfully validate. * * @param userOp UserOp being validated. * @param parsedSignature Signature provided as the userOp.signature parameter (minus the prepended tx type byte). * @param expectedSigner Signer expected to be recovered when decoding the ERC20OPermit signature. */ function validateUserOp(PackedUserOperation calldata userOp, bytes memory parsedSignature, address expectedSigner) internal view returns (uint256) { ( bytes32 superTxHash, bytes32[] memory proof, uint48 lowerBoundTimestamp, uint48 upperBoundTimestamp, bytes memory userEcdsaSignature ) = abi.decode(parsedSignature, (bytes32, bytes32[], uint48, uint48, bytes)); bytes32 calculatedUserOpHash = UserOpLib.getUserOpHash(userOp, lowerBoundTimestamp, upperBoundTimestamp); if (!EcdsaLib.isValidSignature(expectedSigner, superTxHash, userEcdsaSignature)) { return SIG_VALIDATION_FAILED; } if (!MerkleProof.verify(proof, superTxHash, calculatedUserOpHash)) { return SIG_VALIDATION_FAILED; } return _packValidationData(false, upperBoundTimestamp, lowerBoundTimestamp); } function validateSignatureForOwner(address owner, bytes32 hash, bytes memory parsedSignature) internal pure returns (bool) { ( bytes32 superTxHash, //super tx hash bytes32[] memory proof, bytes memory userEcdsaSignature ) = abi.decode(parsedSignature, (bytes32, bytes32[], bytes)); if (!EcdsaLib.isValidSignature(owner, superTxHash, userEcdsaSignature)) { return false; } if (!MerkleProof.verify(proof, superTxHash, hash)) { return false; } return true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import "account-abstraction/interfaces/PackedUserOperation.sol"; import "account-abstraction/core/Helpers.sol"; import "../util/EcdsaLib.sol"; library UserOpValidatorLib { /** * Standard userOp validator - validates by simply checking if the userOpHash was signed by the account's EOA owner. * * @param userOpHash userOpHash being validated. * @param parsedSignature Signature * @param expectedSigner Signer expected to be recovered */ function validateUserOp(bytes32 userOpHash, bytes memory parsedSignature, address expectedSigner) internal pure returns (uint256) { if (!EcdsaLib.isValidSignature(expectedSigner, userOpHash, parsedSignature)) { return SIG_VALIDATION_FAILED; } return SIG_VALIDATION_SUCCESS; } function validateSignatureForOwner(address expectedSigner, bytes32 hash, bytes memory parsedSignature) internal pure returns (bool) { return EcdsaLib.isValidSignature(expectedSigner, hash, parsedSignature); } }
// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity ^0.8.27; library BytesLib { function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore( 0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. ) ) } return tempBytes; } // 0 1 2 3 4 // x x x x x function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol) pragma solidity ^0.8.20; import {Strings} from "../Strings.sol"; /** * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. * * The library provides methods for generating a hash of a message that conforms to the * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] * specifications. */ library MessageHashUtils { /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing a bytes32 `messageHash` with * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with * keccak256, although any bytes32 value can be safely used because the final digest will * be re-hashed. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20) } } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing an arbitrary `message` with * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) { return keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message)); } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x00` (data with intended validator). * * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended * `validator` address. Then hashing the result. * * See {ECDSA-recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked(hex"19_00", validator, data)); } /** * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). * * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with * `\x19\x01` and hashing the result. It corresponds to the hash signed by the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. * * See {ECDSA-recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, hex"19_01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) digest := keccak256(ptr, 0x42) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.20; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the Merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates Merkle trees that are safe * against this attack out of the box. */ library MerkleProof { /** *@dev The multiproof provided is not valid. */ error MerkleProofInvalidMultiproof(); /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Calldata version of {verify} */ function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Calldata version of {processProof} */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Calldata version of {multiProofVerify} * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Calldata version of {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Sorts the pair (a, b) and hashes the result. */ function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } /** * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory. */ function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; /* solhint-disable no-inline-assembly */ /* * For simulation purposes, validateUserOp (and validatePaymasterUserOp) * must return this value in case of signature failure, instead of revert. */ uint256 constant SIG_VALIDATION_FAILED = 1; /* * For simulation purposes, validateUserOp (and validatePaymasterUserOp) * return this value on success. */ uint256 constant SIG_VALIDATION_SUCCESS = 0; /** * Returned data from validateUserOp. * validateUserOp returns a uint256, which is created by `_packedValidationData` and * parsed by `_parseValidationData`. * @param aggregator - address(0) - The account validated the signature by itself. * address(1) - The account failed to validate the signature. * otherwise - This is an address of a signature aggregator that must * be used to validate the signature. * @param validAfter - This UserOp is valid only after this timestamp. * @param validaUntil - This UserOp is valid only up to this timestamp. */ struct ValidationData { address aggregator; uint48 validAfter; uint48 validUntil; } /** * Extract sigFailed, validAfter, validUntil. * Also convert zero validUntil to type(uint48).max. * @param validationData - The packed validation data. */ function _parseValidationData( uint256 validationData ) pure returns (ValidationData memory data) { address aggregator = address(uint160(validationData)); uint48 validUntil = uint48(validationData >> 160); if (validUntil == 0) { validUntil = type(uint48).max; } uint48 validAfter = uint48(validationData >> (48 + 160)); return ValidationData(aggregator, validAfter, validUntil); } /** * Helper to pack the return value for validateUserOp. * @param data - The ValidationData to pack. */ function _packValidationData( ValidationData memory data ) pure returns (uint256) { return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48)); } /** * Helper to pack the return value for validateUserOp, when not using an aggregator. * @param sigFailed - True for signature failure, false for success. * @param validUntil - Last timestamp this UserOperation is valid (or zero for infinite). * @param validAfter - First timestamp this UserOperation is valid. */ function _packValidationData( bool sigFailed, uint48 validUntil, uint48 validAfter ) pure returns (uint256) { return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48)); } /** * keccak function over calldata. * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it. */ function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) { assembly ("memory-safe") { let mem := mload(0x40) let len := data.length calldatacopy(mem, data.offset, len) ret := keccak256(mem, len) } } /** * The minimum of two numbers. * @param a - First number. * @param b - Second number. */ function min(uint256 a, uint256 b) pure returns (uint256) { return a < b ? a : b; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.27; /** * @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[ERC-2612]. * * Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external view returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; library EcdsaLib { using MessageHashUtils for bytes32; using ECDSA for bytes32; function isValidSignature(address expectedSigner, bytes32 hash, bytes memory signature) internal pure returns (bool) { if (_recoverSigner(hash, signature) == expectedSigner) return true; if (_recoverSigner(hash.toEthSignedMessageHash(), signature) == expectedSigner) return true; return false; } function _recoverSigner(bytes32 hash, bytes memory signature) internal pure returns (address) { (address owner,,) = hash.tryRecover(signature); return owner; } }
// SPDX-License-Identifier: Unlicense /* * @title UserOp Lib * * @dev Calculates userOp hash for the new type of transaction - SuperTransaction (as a part of MEE stack) */ pragma solidity ^0.8.27; import "account-abstraction/interfaces/PackedUserOperation.sol"; import "account-abstraction/core/UserOperationLib.sol"; library UserOpLib { using UserOperationLib for PackedUserOperation; /** * Calculates userOp hash. Almost works like a regular 4337 userOp hash with few fields added. * * @param userOp userOp to calculate the hash for * @param lowerBoundTimestamp lower bound timestamp set when constructing userOp * @param upperBoundTimestamp upper bound timestamp set when constructing userOp */ function getUserOpHash( PackedUserOperation calldata userOp, uint256 lowerBoundTimestamp, uint256 upperBoundTimestamp ) internal view returns (bytes32 userOpHash) { userOpHash = keccak256( bytes.concat(keccak256(abi.encode(userOp.hash(), lowerBoundTimestamp, upperBoundTimestamp, block.chainid))) ); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.27; library RLPDecoder { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint256 len; uint256 memPtr; } /* * @param item RLP encoded bytes */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint256 memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @param the RLP item. * @return (memPtr, len) pair: location of the item's payload in memory. */ function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { uint256 offset = _payloadOffset(item.memPtr); uint256 memPtr = item.memPtr + offset; uint256 len = item.len - offset; // data length return (memPtr, len); } /* * @param the RLP item. */ function payloadLen(RLPItem memory item) internal pure returns (uint256) { (, uint256 len) = payloadLocation(item); return len; } /* * @param the RLP item containing the encoded list. */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item)); uint256 items = numItems(item); RLPItem[] memory result = new RLPItem[](items); uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 dataLen; for (uint256 i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } return result; } // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint256 memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /** * RLPItem conversions into data types * */ function toUint(RLPItem memory item) internal pure returns (uint256) { require(item.len > 0 && item.len <= 33); (uint256 memPtr, uint256 len) = payloadLocation(item); uint256 result; assembly { result := mload(memPtr) // shift to the correct location if neccesary if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { require(item.len > 0); (uint256 memPtr, uint256 len) = payloadLocation(item); bytes memory result = new bytes(len); uint256 destPtr; assembly { destPtr := add(0x20, result) } copy(memPtr, destPtr, len); return result; } /* * Private Helpers */ // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) private pure returns (uint256) { if (item.len == 0) return 0; uint256 count = 0; uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint256 memPtr) private pure returns (uint256) { uint256 itemLen; uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) { itemLen = 1; } else if (byte0 < STRING_LONG_START) { itemLen = byte0 - STRING_SHORT_START + 1; } else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len itemLen := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { itemLen = byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length itemLen := add(dataLen, add(byteLen, 1)) } } return itemLen; } // @return number of bytes until the data function _payloadOffset(uint256 memPtr) private pure returns (uint256) { uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) { return 0; } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) { return 1; } else if (byte0 < LIST_SHORT_START) { // being explicit return byte0 - (STRING_LONG_START - 1) + 1; } else { return byte0 - (LIST_LONG_START - 1) + 1; } } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy(uint256 src, uint256 dest, uint256 len) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } if (len > 0) { // left over bytes. Mask is used to remove unwanted bytes from the word uint256 mask = 256 ** (WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.27; import "../util/BytesLib.sol"; /** * @title RLPEncoder * @dev A simple RLP encoding library. * @author Bakaoh */ library RLPEncoder { using BytesLib for bytes; /* * Internal functions */ /** * @dev RLP encodes a byte string. * @param self The byte string to encode. * @return The RLP encoded string in bytes. */ function encodeBytes(bytes memory self) internal pure returns (bytes memory) { bytes memory encoded; if (self.length == 1 && uint8(self[0]) < 128) { encoded = self; } else { encoded = encodeLength(self.length, 128).concat(self); } return encoded; } /** * @dev RLP encodes a uint. * @param self The uint to encode. * @return The RLP encoded uint in bytes. */ function encodeUint(uint256 self) internal pure returns (bytes memory) { return encodeBytes(toBinary(self)); } /** * @dev Encode the first byte, followed by the `len` in binary form if `length` is more than 55. * @param self The length of the string or the payload. * @param offset 128 if item is string, 192 if item is list. * @return RLP encoded bytes. */ function encodeLength(uint256 self, uint256 offset) internal pure returns (bytes memory) { bytes memory encoded; if (self < 56) { encoded = new bytes(1); encoded[0] = bytes32(self + offset)[31]; } else { uint256 lenLen; uint256 i = 1; while (self / i != 0) { lenLen++; i *= 256; } encoded = new bytes(lenLen + 1); encoded[0] = bytes32(lenLen + offset + 55)[31]; for (i = 1; i <= lenLen; i++) { encoded[i] = bytes32((self / (256 ** (lenLen - i))) % 256)[31]; } } return encoded; } /* * Private functions */ /** * @dev Encode integer in big endian binary form with no leading zeroes. * @notice TODO: This should be optimized with assembly to save gas costs. * @param _x The integer to encode. * @return RLP encoded bytes. */ function toBinary(uint256 _x) private pure returns (bytes memory) { bytes memory b = new bytes(32); assembly { mstore(add(b, 32), _x) } uint256 i; for (i = 0; i < 32; i++) { if (b[i] != 0) { break; } } bytes memory res = new bytes(32 - i); for (uint256 j = 0; j < res.length; j++) { res[j] = b[i++]; } return res; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol) pragma solidity ^0.8.20; import {Math} from "./math/Math.sol"; import {SignedMath} from "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; /** * @dev The `value` string doesn't fit in the specified `length`. */ error StringsInsufficientHexLength(uint256 value, uint256 length); /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toStringSigned(int256 value) internal pure returns (string memory) { return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { uint256 localValue = value; bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = HEX_DIGITS[localValue & 0xf]; localValue >>= 4; } if (localValue != 0) { revert StringsInsufficientHexLength(value, length); } return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal * representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.20; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS } /** * @dev The signature derives the `address(0)`. */ error ECDSAInvalidSignature(); /** * @dev The signature has an invalid length. */ error ECDSAInvalidSignatureLength(uint256 length); /** * @dev The signature has an S value that is in the upper half order. */ error ECDSAInvalidSignatureS(bytes32 s); /** * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not * return address(0) without also returning an error description. Errors are documented using an enum (error type) * and a bytes32 providing additional information about the error. * * If no error is returned, then the address can be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { unchecked { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); // We do not check for an overflow here since the shift operation results in 0 or 1. uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError, bytes32) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS, s); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature, bytes32(0)); } return (signer, RecoverError.NoError, bytes32(0)); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s); _throwError(error, errorArg); return recovered; } /** * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. */ function _throwError(RecoverError error, bytes32 errorArg) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert ECDSAInvalidSignature(); } else if (error == RecoverError.InvalidSignatureLength) { revert ECDSAInvalidSignatureLength(uint256(errorArg)); } else if (error == RecoverError.InvalidSignatureS) { revert ECDSAInvalidSignatureS(errorArg); } } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; /* solhint-disable no-inline-assembly */ import "../interfaces/PackedUserOperation.sol"; import {calldataKeccak, min} from "./Helpers.sol"; /** * Utility functions helpful when working with UserOperation structs. */ library UserOperationLib { uint256 public constant PAYMASTER_VALIDATION_GAS_OFFSET = 20; uint256 public constant PAYMASTER_POSTOP_GAS_OFFSET = 36; uint256 public constant PAYMASTER_DATA_OFFSET = 52; /** * Get sender from user operation data. * @param userOp - The user operation data. */ function getSender( PackedUserOperation calldata userOp ) internal pure returns (address) { address data; //read sender from userOp, which is first userOp member (saves 800 gas...) assembly { data := calldataload(userOp) } return address(uint160(data)); } /** * Relayer/block builder might submit the TX with higher priorityFee, * but the user should not pay above what he signed for. * @param userOp - The user operation data. */ function gasPrice( PackedUserOperation calldata userOp ) internal view returns (uint256) { unchecked { (uint256 maxPriorityFeePerGas, uint256 maxFeePerGas) = unpackUints(userOp.gasFees); if (maxFeePerGas == maxPriorityFeePerGas) { //legacy mode (for networks that don't support basefee opcode) return maxFeePerGas; } return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); } } /** * Pack the user operation data into bytes for hashing. * @param userOp - The user operation data. */ function encode( PackedUserOperation calldata userOp ) internal pure returns (bytes memory ret) { address sender = getSender(userOp); uint256 nonce = userOp.nonce; bytes32 hashInitCode = calldataKeccak(userOp.initCode); bytes32 hashCallData = calldataKeccak(userOp.callData); bytes32 accountGasLimits = userOp.accountGasLimits; uint256 preVerificationGas = userOp.preVerificationGas; bytes32 gasFees = userOp.gasFees; bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData); return abi.encode( sender, nonce, hashInitCode, hashCallData, accountGasLimits, preVerificationGas, gasFees, hashPaymasterAndData ); } function unpackUints( bytes32 packed ) internal pure returns (uint256 high128, uint256 low128) { return (uint128(bytes16(packed)), uint128(uint256(packed))); } //unpack just the high 128-bits from a packed value function unpackHigh128(bytes32 packed) internal pure returns (uint256) { return uint256(packed) >> 128; } // unpack just the low 128-bits from a packed value function unpackLow128(bytes32 packed) internal pure returns (uint256) { return uint128(uint256(packed)); } function unpackMaxPriorityFeePerGas(PackedUserOperation calldata userOp) internal pure returns (uint256) { return unpackHigh128(userOp.gasFees); } function unpackMaxFeePerGas(PackedUserOperation calldata userOp) internal pure returns (uint256) { return unpackLow128(userOp.gasFees); } function unpackVerificationGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { return unpackHigh128(userOp.accountGasLimits); } function unpackCallGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { return unpackLow128(userOp.accountGasLimits); } function unpackPaymasterVerificationGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET])); } function unpackPostOpGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET])); } function unpackPaymasterStaticFields( bytes calldata paymasterAndData ) internal pure returns (address paymaster, uint256 validationGasLimit, uint256 postOpGasLimit) { return ( address(bytes20(paymasterAndData[: PAYMASTER_VALIDATION_GAS_OFFSET])), uint128(bytes16(paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET])), uint128(bytes16(paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET])) ); } /** * Hash the user operation data. * @param userOp - The user operation data. */ function hash( PackedUserOperation calldata userOp ) internal pure returns (bytes32) { return keccak256(encode(userOp)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.20; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
{ "remappings": [ "openzeppelin/=node_modules/@openzeppelin/contracts/", "account-abstraction/=node_modules/account-abstraction/contracts/", "erc7739Validator/=node_modules/erc7739-validator-base/src/", "solady/=node_modules/solady/src/", "forge-std/=lib/forge-std/src/", "@ERC4337/=node_modules/@ERC4337/", "@erc7579/=node_modules/@erc7579/", "@gnosis.pm/=node_modules/@gnosis.pm/", "@openzeppelin/=node_modules/@openzeppelin/", "@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/", "enumerableset4337/=node_modules/erc7739-validator-base/node_modules/@erc7579/enumerablemap4337/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/", "excessively-safe-call/=node_modules/excessively-safe-call/", "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/erc7739-validator-base/node_modules/@rhinestone/modulekit/src/", "safe7579/=node_modules/erc7739-validator-base/node_modules/@rhinestone/safe7579/src/", "sentinellist/=node_modules/erc7739-validator-base/node_modules/@rhinestone/sentinellist/src/", "solarray/=node_modules/solarray/" ], "optimizer": { "enabled": true, "runs": 999 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"InvalidTargetAddress","type":"error"},{"inputs":[],"name":"ModuleAlreadyInitialized","type":"error"},{"inputs":[],"name":"NewOwnerIsContract","type":"error"},{"inputs":[],"name":"NoOwnerProvided","type":"error"},{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"NotInitialized","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":"sigValidationResult","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":"pure","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":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]
Contract Creation Code
6080604052348015600e575f5ffd5b5061398f8061001c5f395ff3fe608060405234801561000f575f5ffd5b50600436106100f0575f3560e01c8063940d384011610093578063e824b56811610063578063e824b56814610283578063ecd0596114610296578063f2fde38b146102aa578063f551e2ee146102bd575f5ffd5b8063940d3840146102165780639700320314610229578063d60b347f1461024a578063d620c85a1461025d575f5ffd5b806354fd4d50116100ce57806354fd4d50146101a25780635c81ca68146101db5780636d61fe70146101f05780638a91b0e314610203575f5ffd5b806306fdde03146100f45780630807dbc11461013f5780632e5b63a614610162575b5f5ffd5b60408051808201909152600e81527f4b314d656556616c696461746f7200000000000000000000000000000000000060208201525b6040516101369190612f01565b60405180910390f35b61015261014d366004612f4d565b610301565b6040519015158152602001610136565b61018a610170366004612f84565b5f602081905290815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610136565b60408051808201909152600581527f312e302e300000000000000000000000000000000000000000000000000000006020820152610129565b6101ee6101e9366004612f84565b610317565b005b6101ee6101fe366004612fdd565b610327565b6101ee610211366004612fdd565b61047a565b61015261022436600461301c565b6104af565b61023c610237366004613095565b61051d565b604051908152602001610136565b610152610258366004612f84565b61055f565b7fd620c85a0000000000000000000000000000000000000000000000000000000061023c565b6101ee610291366004612f84565b610582565b6101526102a43660046130dc565b60011490565b6101ee6102b8366004612f84565b61058e565b6102d06102cb3660046130f3565b610629565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610136565b5f61030e60018385610647565b90505b92915050565b6103236001338361067d565b5050565b5f819003610361576040517f1f2a381c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b335f908152602081905260409020546001600160a01b0316156103b0576040517fe72ce85e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6103be601482848661314b565b6103c791613172565b60601c905080610403576040517fc81abf6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b803b15610423576040516383e6a1cb60e01b815260040160405180910390fd5b335f908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038316179055601482111561047557610475610470836014818761314b565b610692565b505050565b335f818152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19169055610323906001906106f9565b5f601482146104ea576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6104f8601482858761314b565b61050191613172565b60601c905061051281888888610703565b979650505050505050565b5f80808061052e6020870187612f84565b6001600160a01b03908116825260208201929092526040015f2054169050610557848483610744565b949350505050565b6001600160a01b038082165f908152602081905260408120549091161515610311565b610323600133836108b0565b6001600160a01b0381166105ce576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b803b156105ee576040516383e6a1cb60e01b815260040160405180910390fd5b335f908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b5f61063e858561063986866108c5565b6108f5565b95945050505050565b6001600160a01b038181165f908152600185016020908152604080832093861683529290529081205415155b90505b9392505050565b5f61067384846001600160a01b0385166109d7565b5f5b61069f6014836131da565b811015610475576106f03384846106b78560146131ed565b906106c3866001613204565b6106ce9060146131ed565b926106db9392919061314b565b6106e491613172565b6001919060601c61067d565b50600101610694565b6103238282610a5c565b5f61063e858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610ab092505050565b5f80610790610757610100870187613217565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610b6092505050565b90505f815160038111156107a6576107a661325a565b036107c2576107ba85826020015185610d7e565b915050610676565b6001815160038111156107d7576107d761325a565b036107eb576107ba85826020015185610e15565b6002815160038111156108005761080061325a565b03610814576107ba85826020015185610ef0565b6003815160038111156108295761082961325a565b0361083d576107ba84826020015185611147565b60405162461bcd60e51b815260206004820152603260248201527f53757065725478456364736156616c696461746f724c69623a3a20696e76616c60448201527f696420757365724f70207369672074797065000000000000000000000000000060648201526084015b60405180910390fd5b5f61067384846001600160a01b038516611168565b818161649261ffff30801c190402818301601f190135036108ee57506040810135016020810190355b9250929050565b5f816109325761773961ffff83190402840361093257507f7739000100000000000000000000000000000000000000000000000000000000610557565b60208301357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610991576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61099e87878787611246565b806109af57506109af868686611260565b806109c057506109c08686866114fe565b155f03631626ba7e1760e01b979650505050505050565b5f81815260018401602090815260408083206001600160a01b0386168452909152812054610a55575f838152602085815260409091208054600101918202810184905555610a258484611569565b5f8381526001808701602090815260408084206001600160a01b0389168552909152909120919091559050610676565b505f610676565b5f610a67838361157c565b905060015b818111610aaa575f610a888585610a83858761326e565b611587565b9050610a95858583611168565b50508080610aa290613281565b915050610a6c565b50505050565b5f5f610abb83610b60565b90505f81516003811115610ad157610ad161325a565b03610ae5576107ba85858360200151611593565b600181516003811115610afa57610afa61325a565b03610b0e576107ba858583602001516115f2565b600281516003811115610b2357610b2361325a565b03610b37576107ba8585836020015161169a565b600381516003811115610b4c57610b4c61325a565b0361083d576107ba858583602001516117ec565b604080518082019091525f8152606060208201525f610b8f6001808551610b87919061326e565b8591906117f8565b90505f60ff16835f81518110610ba757610ba7613299565b016020015160f81c03610bd05760408051808201909152805f5b81526020019190915292915050565b600160ff16835f81518110610be757610be7613299565b016020015160f81c03610c065760408051808201909152806001610bc1565b600260ff16835f81518110610c1d57610c1d613299565b016020015160f81c03610c3c5760408051808201909152806002610bc1565b60ff8016835f81518110610c5257610c52613299565b016020015160f81c03610c715760408051808201909152806003610bc1565b60405162461bcd60e51b81526020600482015260b360248201527f53757065725478456364736156616c696461746f724c69623a3a20696e76616c60448201527f69642073696720747970652e204578706563746564207072656669782030783060648201527f3020666f72206f66662d636861696e2c203078303120666f72206f6e2d63686160848201527f696e206f72203078303220666f72206572633230207065726d6974206974782060a48201527f68617368207369676e6174757265206f72203078666620666f72206e6f726d6160c48201527f6c20757365724f70207369676e61747572652e0000000000000000000000000060e4820152610104016108a7565b50919050565b5f5f5f5f5f5f87806020019051810190610d989190613430565b945094509450945094505f610dbe8a8565ffffffffffff168565ffffffffffff1661191f565b9050610dcb888784611986565b610dde5760019650505050505050610676565b610de9858783611a0a565b610dfc5760019650505050505050610676565b610e075f8486611a1f565b9a9950505050505050505050565b5f5f610e2084611a55565b90505f610e47868360e0015165ffffffffffff1684610100015165ffffffffffff1661191f565b90505f826040015183606001518460200151604051602001610e8993929190928352602083019190915260f81b6001600160f81b031916604082015260410190565b6040516020818303038152906040529050610ea985846080015183611986565b610eb95760019350505050610676565b610ecc8360c001518460a0015184611a0a565b610edc5760019350505050610676565b6105125f8461010001518560e00151611a1f565b5f5f83806020019051810190610f0691906134d9565b90505f610f2e8683610140015165ffffffffffff1684610160015165ffffffffffff1661191f565b90505f610f3f836101800151611cae565b6101008401516040808601516020808801516080808a015160c0808c01518751958601969096526001600160a01b03808f169786019790975295909216606084015282015260a0810191909152908101829052919250905f9060e0016040516020818303038152906040528051906020012090505f610fc2828760600151611d08565b6101a08701516101c08801516040805160208101939093528201526001600160f81b031960f887901b1660608201529091505f906061016040516020818303038152906040529050611015898383611986565b611029576001975050505050505050610676565b61103e87610120015188610100015188611a0a565b611052576001975050505050505050610676565b8660e00151156111235786516001600160a01b031663d505accf8a61107a60208f018f612f84565b60808b01516101a08c01516101c08d015160405160e087901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b03958616600482015294909316602485015260448401919091526064830189905260ff8a16608484015260a483015260c482015260e4015f604051808303815f87803b15801561110c575f5ffd5b505af115801561111e573d5f5f3e3d5ffd5b505050505b6111385f886101600151896101400151611a1f565b9b9a5050505050505050505050565b5f611153828585611986565b61115f57506001610676565b505f9392505050565b5f81815260018401602090815260408083206001600160a01b0386168452909152812054801561123d575f61119e60018361326e565b90505f60016111ad8888611569565b6111b7919061326e565b9050808214611201575f6111cc888884611d47565b90506111da88888584611d53565b5f90815260018801602090815260408083206001600160a01b038a16845290915290208390555b61120b8787611d5f565b5050505f8281526001808601602090815260408084206001600160a01b03881685529091528220919091559050610676565b5f915050610676565b5f61125085611d69565b156105575761063e848484611da9565b5f308015611319575f5f5f5f5f336001600160a01b03166384b0196e6040518163ffffffff1660e01b81526004015f60405180830381865afa1580156112a8573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526112cf9190810190613601565b506040805186516020978801208183015285519590960194909420606086015260808501929092526001600160a01b031660a084015260c083015260e08201905296505050505050505b6040516002840385013560f01c8060420180860387016119015f52604081602037821582881017896042601e20181715611384575f94507f983e65e5148e570cd828ead231ee759a8d7958721a768f93bc4483ba005c32de85528860205260408520985050506114dd565b7f5479706564446174615369676e280000000000000000000000000000000000008452600e84018360408301823760288185019081525f1901515f1a6029146113fd575f6001858301035b6001820191506029828203515f1a14868310116113cf57508085039450808560408501018337602881830153505b6f07fffffe00000000000001000000000081515f1a1c5b602882515f1a1461143a57806512010000000183515f1a1c179050600182019150611414565b7f20636f6e74656e74732c737472696e67206e616d652c737472696e670000000082527f2076657273696f6e2c75696e7432353620636861696e49642c61646472657373601c8301527f20766572696679696e67436f6e74726163742c627974657333322073616c7429603c830152605c820191508460408401833760408388379084018590038520865260e08620604052600116604201601e20985050909403935b50604052806114f3576114f03386611dcd565b94505b61063e858585611da9565b5f3a610676573a3a526d378edcd5b5b0a24f5342d8c1048560203a3a388461fffffa503a5161155d57604051631626ba7e3a528160205260408052454561ffff0117805a10838814171561154e57fe5b3a3a6064601c3085fa50506040525b50610673848484611da9565b5f8181526020839052604081205461030e565b5f61030e8383611569565b5f610673848484611d47565b5f5f5f5f848060200190518101906115ab91906136d4565b9250925092506115bc878483611986565b6115cb575f9350505050610676565b6115d6828488611a0a565b6115e5575f9350505050610676565b5060019695505050505050565b5f5f6115fd83611a55565b90505f81604001518260600151836020015160405160200161163f93929190928352602083019190915260f81b6001600160f81b031916604082015260410190565b604051602081830303815290604052905061165f86836080015183611986565b61166d575f92505050610676565b6116808260c001518360a0015187611a0a565b61168e575f92505050610676565b50600195945050505050565b5f5f828060200190518101906116b091906134d9565b90505f6116c1826101800151611cae565b90505f8261010001515f1c90505f836040015188856020015186608001518760c0015186604051602001611729969594939291909586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b6040516020818303038152906040528051906020012090505f611750828660600151611d08565b6101a08601516101c08701516040805160208101939093528201526001600160f81b031960f887901b1660608201529091505f9060610160405160208183030381529060405290506117a38a8383611986565b6117b5575f9650505050505050610676565b6117ca8661012001518761010001518b611a0a565b6117dc575f9650505050505050610676565b5060019998505050505050505050565b5f610673848484611986565b60608161180681601f613204565b10156118545760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f7700000000000000000000000000000000000060448201526064016108a7565b61185e8284613204565b845110156118ae5760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e647300000000000000000000000000000060448201526064016108a7565b6060821580156118cc5760405191505f825260208201604052611916565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156119055780518352602092830192016118ed565b5050858452601f01601f1916604052505b50949350505050565b5f61192984611eb8565b60408051602081019290925281018490526060810183905246608082015260a00160408051601f19818403018152828252805160209182012090830152016040516020818303038152906040528051906020012090509392505050565b5f836001600160a01b031661199b8484611ed0565b6001600160a01b0316036119b157506001610676565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f908152601c849052603c90206001600160a01b038516906119f49084611ed0565b6001600160a01b03160361115f57506001610676565b5f82611a168584611ee7565b14949350505050565b5f60d08265ffffffffffff16901b60a08465ffffffffffff16901b85611a45575f611a48565b60015b60ff161717949350505050565b60408051610120810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c082015260e081018290526101008101919091525f825f81518110611aaf57611aaf613299565b016020015160f81c90505f611ae1611ac960066002613742565b60ff168551611ad8919061326e565b859060066117f8565b611aea9061375e565b60d01c90505f611b0d600660ff168651611b04919061326e565b869060066117f8565b611b169061375e565b60d01c90505f856001611b2b60066002613742565b60ff168851611b3a919061326e565b611b44919061326e565b81518110611b5457611b54613299565b016020015160f81c90505f611b6b60066002613742565b60ff16602060ff168360ff16611b8191906131ed565b611b8c906001613204565b611b969190613204565b90505f611bbe600180848b51611bac919061326e565b611bb6919061326e565b8a91906117f8565b90505f611bf1826040805180820182525f8082526020918201528151808301909252825182529182019181019190915290565b90505f611bfd82611f29565b90505f611c0a898361202f565b90506040518061012001604052808a60ff168152602001611c2d835f0151611cae565b60ff1681526020018260200151815260200182604001518152602001611c5e8b87611c57886121aa565b86516121b5565b8152602001611c70836060015161234e565b8152602001611c7f8d896123ee565b81526020018965ffffffffffff1681526020018865ffffffffffff168152509950505050505050505050919050565b5f60258210611cec576023611cc2836124cf565b611ccd9060026131ed565b611cd7908461326e565b611ce1919061326e565b61031190601b613204565b60018211611cff5761031182601b613204565b5090565b919050565b6040517f190100000000000000000000000000000000000000000000000000000000000081526002810182905260228101839052604290205f9061030e565b5f6106738484846124e7565b610aaa848484846124fe565b6103238282612516565b5f6dd9ecebf3c23529de49815dac1c4c6001600160a01b0383161480611d9757506001600160a01b03821633145b80610311575061031160013384610647565b335f90815260208190526040812054610673906001600160a01b0316858585610703565b5f5f5f5f5f866001600160a01b03166384b0196e6040518163ffffffff1660e01b81526004015f60405180830381865afa158015611e0d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611e349190810190613601565b50509450945094509450506040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815284516020860120602082015283516020850120604082015282606082015281608082015260a081209550506719010000000000005f5284601a5285603a52604260182094505f603a525050505092915050565b5f611ec282612552565b805190602001209050919050565b5f80611edc8484612607565b509095945050505050565b5f81815b8451811015611f2157611f1782868381518110611f0a57611f0a613299565b6020026020010151612650565b9150600101611eeb565b509392505050565b6060611f348261267c565b611f3c575f5ffd5b5f611f46836126b3565b90505f8167ffffffffffffffff811115611f6257611f626132ad565b604051908082528060200260200182016040528015611fa657816020015b604080518082019091525f8082526020820152815260200190600190039081611f805790505b5090505f611fb78560200151612736565b8560200151611fc69190613204565b90505f805b8481101561202457611fdc836127af565b915060405180604001604052808381526020018481525084828151811061200557612005613299565b602090810291909101015261201a8284613204565b9250600101611fcb565b509195945050505050565b61205760405180608001604052805f81526020015f81526020015f8152602001606081525090565b5f80808060ff871661207757506005925060069150600790506008612105565b60011960ff88160161209757506007925060099150600a9050600b612105565b60405162461bcd60e51b815260206004820152602860248201527f547856616c696461746f724c69623a3a20756e737570706f727465642065766d60448201527f207478207479706500000000000000000000000000000000000000000000000060648201526084016108a7565b6040518060800160405280612135888660ff168151811061212857612128613299565b6020026020010151612852565b8152602001612152888560ff168151811061212857612128613299565b5f1b8152602001612171888460ff168151811061212857612128613299565b5f1b815260200161219d888760ff168151811061219057612190613299565b602002602001015161289c565b9052979650505050505050565b5f5f6105578361290e565b5f5f6121c083612950565b516121cc906042613204565b90505f8486516121dc919061326e565b90505f6122038283858a516121f1919061326e565b6121fb919061326e565b8991906117f8565b905060011960ff89160161225b578761222a8260405180602001604052805f815250612963565b60405160200161223b9291906137e1565b604051602081830303815290604052805190602001209350505050610557565b60ff88166122e157602585106122c9576122b88161228061227b886124cf565b612950565b6122895f612950565b6122925f612950565b6040516020016122a4939291906137ff565b604051602081830303815290604052612963565b805190602001209350505050610557565b6122b88160405180602001604052805f815250612963565b60405162461bcd60e51b8152602060048201526024808201527f547856616c696461746f724c69623a3a20756e737570706f727465642074782060448201527f747970650000000000000000000000000000000000000000000000000000000060648201526084016108a7565b5f602060ff16825110156123ca5760405162461bcd60e51b815260206004820152602560248201527f54784465636f6465723a3a2063616c6c44617461206c656e67746820746f6f2060448201527f73686f727400000000000000000000000000000000000000000000000000000060648201526084016108a7565b81516123e5906123dc9060209061326e565b839060206117f8565b6103119061381c565b60608160ff1667ffffffffffffffff81111561240c5761240c6132ad565b604051908082528060200260200182016040528015612435578160200160208202803683370190505b5090505f600161244760066002613742565b60ff168551612456919061326e565b612460919061326e565b90505b60ff8316156124c85761248361247a60208361326e565b859060206117f8565b61248c9061381c565b8261249860018661383f565b60ff16815181106124ab576124ab613299565b6020908102919091010152826124c081613858565b935050612463565b5092915050565b5f60026124dd60238461326e565b61031191906131da565b5f82815260208490526040812061067390836129c3565b5f838152602085905260409020610aaa9083836129e9565b5f818152602083905260408120805490918190036125345750505050565b6125498261254360018461326e565b5f6129e9565b5f190190555050565b6060813560208301355f61257161256c6040870187613217565b612a12565b90505f61258461256c6060880188613217565b9050608086013560a087013560c08801355f6125a661256c60e08c018c613217565b604080516001600160a01b039a909a1660208b015289810198909852606089019690965250608087019390935260a086019190915260c085015260e08401526101008084019190915281518084039091018152610120909201905292915050565b5f5f5f835160410361263e576020840151604085015160608601515f1a61263088828585612a24565b955095509550505050612649565b505081515f91506002905b9250925092565b5f81831061266a575f82815260208490526040902061030e565b5f83815260208390526040902061030e565b80515f90810361268d57505f919050565b602082015180515f1a9060c08210156126a957505f9392505050565b5060019392505050565b80515f9081036126c457505f919050565b5f5f90505f6126d68460200151612736565b84602001516126e59190613204565b90505f845f015185602001516126fb9190613204565b90505b8082101561272d5761270f826127af565b6127199083613204565b91508261272581613281565b9350506126fe565b50909392505050565b80515f90811a608081101561274d57505f92915050565b60b8811080612768575060c08110801590612768575060f881105b156127765750600192915050565b60c08110156127a35761278b600160b861383f565b6127989060ff168261326e565b610676906001613204565b61278b600160f861383f565b80515f908190811a60808110156127c957600191506124c8565b60b88110156127ef576127dd60808261326e565b6127e8906001613204565b91506124c8565b60c081101561281c5760b78103600185019450806020036101000a855104600182018101935050506124c8565b60f8811015612830576127dd60c08261326e565b60019390930151602084900360f7016101000a900490920160f5190192915050565b80515f901580159061286657508151602110155b61286e575f5ffd5b5f5f6128798461290e565b8151919350915060208210156105575760208290036101000a9004949350505050565b80516060906128a9575f5ffd5b5f5f6128b48461290e565b915091505f8167ffffffffffffffff8111156128d2576128d26132ad565b6040519080825280601f01601f1916602001820160405280156128fc576020820181803683370190505b50905060208101611916848285612aec565b5f5f5f61291e8460200151612736565b90505f8185602001516129319190613204565b90505f82865f0151612943919061326e565b9196919550909350505050565b606061031161295e83612b6c565b612c84565b60605f8383604051602001612979929190613873565b60408051601f1981840301815291905280519091506129999060c0612cd3565b816040516020016129ab929190613873565b60405160208183030381529060405291505092915050565b5f825482106129dd57638277484f5f52816020526024601cfd5b50600101602002015490565b82548210612a0257638277484f5f52816020526024601cfd5b8060018301602002840155505050565b5f604051828085833790209392505050565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115612a5d57505f91506003905082612ae2565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015612aae573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b038116612ad957505f925060019150829050612ae2565b92505f91508190505b9450945094915050565b805f03612af857505050565b60208110612b305782518252612b0f602084613204565b9250612b1c602083613204565b9150612b2960208261326e565b9050612af8565b8015610475575f6001612b4483602061326e565b612b5090610100613964565b612b5a919061326e565b84518451821691191617835250505050565b6040805160208082528183019092526060915f91906020820181803683375050506020810184905290505f5b6020811015612bd057818181518110612bb357612bb3613299565b01602001516001600160f81b0319165f03612bd057600101612b98565b5f612bdc82602061326e565b67ffffffffffffffff811115612bf457612bf46132ad565b6040519080825280601f01601f191660200182016040528015612c1e576020820181803683370190505b5090505f5b8151811015611916578383612c3781613281565b945081518110612c4957612c49613299565b602001015160f81c60f81b828281518110612c6657612c66613299565b60200101906001600160f81b03191690815f1a905350600101612c23565b60608082516001148015612cb157506080835f81518110612ca757612ca7613299565b016020015160f81c105b15612cbd575081610311565b61030e83612ccd85516080612cd3565b90612e84565b6060806038841015612d3b5760408051600180825281830190925290602082018180368337019050509050612d088385613204565b601f1a60f81b815f81518110612d2057612d20613299565b60200101906001600160f81b03191690815f1a90535061030e565b5f60015b612d4981876131da565b15612d6f5781612d5881613281565b9250612d689050610100826131ed565b9050612d3f565b612d7a826001613204565b67ffffffffffffffff811115612d9257612d926132ad565b6040519080825280601f01601f191660200182016040528015612dbc576020820181803683370190505b509250612dc98583613204565b612dd4906037613204565b601f1a60f81b835f81518110612dec57612dec613299565b60200101906001600160f81b03191690815f1a905350600190505b818111612e7b57610100612e1b828461326e565b612e2790610100613964565b612e3190886131da565b612e3b919061396f565b601f1a60f81b838281518110612e5357612e53613299565b60200101906001600160f81b03191690815f1a90535080612e7381613281565b915050612e07565b50509392505050565b6060806040519050835180825260208201818101602087015b81831015612eb5578051835260209283019201612e9d565b50855184518101855292509050808201602086015b81831015612ee2578051835260209283019201612eca565b508651929092011591909101601f01601f191660405250905092915050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b6001600160a01b0381168114612f4a575f5ffd5b50565b5f5f60408385031215612f5e575f5ffd5b8235612f6981612f36565b91506020830135612f7981612f36565b809150509250929050565b5f60208284031215612f94575f5ffd5b813561030e81612f36565b5f5f83601f840112612faf575f5ffd5b50813567ffffffffffffffff811115612fc6575f5ffd5b6020830191508360208285010111156108ee575f5ffd5b5f5f60208385031215612fee575f5ffd5b823567ffffffffffffffff811115613004575f5ffd5b61301085828601612f9f565b90969095509350505050565b5f5f5f5f5f60608688031215613030575f5ffd5b85359450602086013567ffffffffffffffff81111561304d575f5ffd5b61305988828901612f9f565b909550935050604086013567ffffffffffffffff811115613078575f5ffd5b61308488828901612f9f565b969995985093965092949392505050565b5f5f604083850312156130a6575f5ffd5b823567ffffffffffffffff8111156130bc575f5ffd5b830161012081860312156130ce575f5ffd5b946020939093013593505050565b5f602082840312156130ec575f5ffd5b5035919050565b5f5f5f5f60608587031215613106575f5ffd5b843561311181612f36565b935060208501359250604085013567ffffffffffffffff811115613133575f5ffd5b61313f87828801612f9f565b95989497509550505050565b5f5f85851115613159575f5ffd5b83861115613165575f5ffd5b5050820193919092039150565b80356bffffffffffffffffffffffff1981169060148410156124c8576bffffffffffffffffffffffff1960149490940360031b84901b1690921692915050565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f826131e8576131e86131b2565b500490565b8082028115828204841417610311576103116131c6565b80820180821115610311576103116131c6565b5f5f8335601e1984360301811261322c575f5ffd5b83018035915067ffffffffffffffff821115613246575f5ffd5b6020019150368190038213156108ee575f5ffd5b634e487b7160e01b5f52602160045260245ffd5b81810381811115610311576103116131c6565b5f60018201613292576132926131c6565b5060010190565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e0810167ffffffffffffffff811182821017156132e5576132e56132ad565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613314576133146132ad565b604052919050565b5f67ffffffffffffffff821115613335576133356132ad565b5060051b60200190565b5f82601f83011261334e575f5ffd5b815161336161335c8261331c565b6132eb565b8082825260208201915060208360051b860101925085831115613382575f5ffd5b602085015b8381101561339f578051835260209283019201613387565b5095945050505050565b805165ffffffffffff81168114611d03575f5ffd5b5f82601f8301126133cd575f5ffd5b8151602083015f5f67ffffffffffffffff8411156133ed576133ed6132ad565b50601f8301601f1916602001613402816132eb565b915050828152858383011115613416575f5ffd5b8282602083015e5f92810160200192909252509392505050565b5f5f5f5f5f60a08688031215613444575f5ffd5b8551602087015190955067ffffffffffffffff811115613462575f5ffd5b61346e8882890161333f565b94505061347d604087016133a9565b925061348b606087016133a9565b9150608086015167ffffffffffffffff8111156134a6575f5ffd5b6134b2888289016133be565b9150509295509295909350565b8051611d0381612f36565b80518015158114611d03575f5ffd5b5f602082840312156134e9575f5ffd5b815167ffffffffffffffff8111156134ff575f5ffd5b82016101e08185031215613511575f5ffd5b6135196132c1565b613522826134bf565b8152613530602083016134bf565b602082015260408281015190820152606080830151908201526080808301519082015260a0808301519082015260c0808301519082015261357360e083016134ca565b60e0820152610100828101519082015261012082015167ffffffffffffffff81111561359d575f5ffd5b6135a98682850161333f565b610120830152506135bd61014083016133a9565b6101408201526135d061016083016133a9565b61016082015261018082810151908201526101a080830151908201526101c091820151918101919091529392505050565b5f5f5f5f5f5f5f60e0888a031215613617575f5ffd5b87516001600160f81b03198116811461362e575f5ffd5b602089015190975067ffffffffffffffff81111561364a575f5ffd5b6136568a828b016133be565b965050604088015167ffffffffffffffff811115613672575f5ffd5b61367e8a828b016133be565b60608a015190965094506136969050608089016134bf565b60a089015160c08a0151919450925067ffffffffffffffff8111156136b9575f5ffd5b6136c58a828b0161333f565b91505092959891949750929550565b5f5f5f606084860312156136e6575f5ffd5b8351602085015190935067ffffffffffffffff811115613704575f5ffd5b6137108682870161333f565b925050604084015167ffffffffffffffff81111561372c575f5ffd5b613738868287016133be565b9150509250925092565b60ff81811683821602908116908181146124c8576124c86131c6565b805160208201517fffffffffffff00000000000000000000000000000000000000000000000000008116919060068210156137c3577fffffffffffff0000000000000000000000000000000000000000000000000000808360060360031b1b82161692505b5050919050565b5f81518060208401855e5f93019283525090919050565b6001600160f81b03198360f81b1681525f61067360018301846137ca565b5f61063e61381661381084886137ca565b866137ca565b846137ca565b80516020808301519190811015610d78575f1960209190910360031b1b16919050565b60ff8281168282160390811115610311576103116131c6565b5f60ff82168061386a5761386a6131c6565b5f190192915050565b5f61067361381683866137ca565b6001815b60018411156138bc578085048111156138a0576138a06131c6565b60018416156138ae57908102905b60019390931c928002613885565b935093915050565b5f826138d257506001610311565b816138de57505f610311565b81600181146138f457600281146138fe5761391a565b6001915050610311565b60ff84111561390f5761390f6131c6565b50506001821b610311565b5060208310610133831016604e8410600b841016171561393d575081810a610311565b6139495f198484613881565b805f190482111561395c5761395c6131c6565b029392505050565b5f61030e83836138c4565b5f8261397d5761397d6131b2565b50069056fea164736f6c634300081b000a
Deployed Bytecode
0x608060405234801561000f575f5ffd5b50600436106100f0575f3560e01c8063940d384011610093578063e824b56811610063578063e824b56814610283578063ecd0596114610296578063f2fde38b146102aa578063f551e2ee146102bd575f5ffd5b8063940d3840146102165780639700320314610229578063d60b347f1461024a578063d620c85a1461025d575f5ffd5b806354fd4d50116100ce57806354fd4d50146101a25780635c81ca68146101db5780636d61fe70146101f05780638a91b0e314610203575f5ffd5b806306fdde03146100f45780630807dbc11461013f5780632e5b63a614610162575b5f5ffd5b60408051808201909152600e81527f4b314d656556616c696461746f7200000000000000000000000000000000000060208201525b6040516101369190612f01565b60405180910390f35b61015261014d366004612f4d565b610301565b6040519015158152602001610136565b61018a610170366004612f84565b5f602081905290815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610136565b60408051808201909152600581527f312e302e300000000000000000000000000000000000000000000000000000006020820152610129565b6101ee6101e9366004612f84565b610317565b005b6101ee6101fe366004612fdd565b610327565b6101ee610211366004612fdd565b61047a565b61015261022436600461301c565b6104af565b61023c610237366004613095565b61051d565b604051908152602001610136565b610152610258366004612f84565b61055f565b7fd620c85a0000000000000000000000000000000000000000000000000000000061023c565b6101ee610291366004612f84565b610582565b6101526102a43660046130dc565b60011490565b6101ee6102b8366004612f84565b61058e565b6102d06102cb3660046130f3565b610629565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610136565b5f61030e60018385610647565b90505b92915050565b6103236001338361067d565b5050565b5f819003610361576040517f1f2a381c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b335f908152602081905260409020546001600160a01b0316156103b0576040517fe72ce85e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6103be601482848661314b565b6103c791613172565b60601c905080610403576040517fc81abf6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b803b15610423576040516383e6a1cb60e01b815260040160405180910390fd5b335f908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038316179055601482111561047557610475610470836014818761314b565b610692565b505050565b335f818152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19169055610323906001906106f9565b5f601482146104ea576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6104f8601482858761314b565b61050191613172565b60601c905061051281888888610703565b979650505050505050565b5f80808061052e6020870187612f84565b6001600160a01b03908116825260208201929092526040015f2054169050610557848483610744565b949350505050565b6001600160a01b038082165f908152602081905260408120549091161515610311565b610323600133836108b0565b6001600160a01b0381166105ce576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b803b156105ee576040516383e6a1cb60e01b815260040160405180910390fd5b335f908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b5f61063e858561063986866108c5565b6108f5565b95945050505050565b6001600160a01b038181165f908152600185016020908152604080832093861683529290529081205415155b90505b9392505050565b5f61067384846001600160a01b0385166109d7565b5f5b61069f6014836131da565b811015610475576106f03384846106b78560146131ed565b906106c3866001613204565b6106ce9060146131ed565b926106db9392919061314b565b6106e491613172565b6001919060601c61067d565b50600101610694565b6103238282610a5c565b5f61063e858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610ab092505050565b5f80610790610757610100870187613217565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610b6092505050565b90505f815160038111156107a6576107a661325a565b036107c2576107ba85826020015185610d7e565b915050610676565b6001815160038111156107d7576107d761325a565b036107eb576107ba85826020015185610e15565b6002815160038111156108005761080061325a565b03610814576107ba85826020015185610ef0565b6003815160038111156108295761082961325a565b0361083d576107ba84826020015185611147565b60405162461bcd60e51b815260206004820152603260248201527f53757065725478456364736156616c696461746f724c69623a3a20696e76616c60448201527f696420757365724f70207369672074797065000000000000000000000000000060648201526084015b60405180910390fd5b5f61067384846001600160a01b038516611168565b818161649261ffff30801c190402818301601f190135036108ee57506040810135016020810190355b9250929050565b5f816109325761773961ffff83190402840361093257507f7739000100000000000000000000000000000000000000000000000000000000610557565b60208301357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610991576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61099e87878787611246565b806109af57506109af868686611260565b806109c057506109c08686866114fe565b155f03631626ba7e1760e01b979650505050505050565b5f81815260018401602090815260408083206001600160a01b0386168452909152812054610a55575f838152602085815260409091208054600101918202810184905555610a258484611569565b5f8381526001808701602090815260408084206001600160a01b0389168552909152909120919091559050610676565b505f610676565b5f610a67838361157c565b905060015b818111610aaa575f610a888585610a83858761326e565b611587565b9050610a95858583611168565b50508080610aa290613281565b915050610a6c565b50505050565b5f5f610abb83610b60565b90505f81516003811115610ad157610ad161325a565b03610ae5576107ba85858360200151611593565b600181516003811115610afa57610afa61325a565b03610b0e576107ba858583602001516115f2565b600281516003811115610b2357610b2361325a565b03610b37576107ba8585836020015161169a565b600381516003811115610b4c57610b4c61325a565b0361083d576107ba858583602001516117ec565b604080518082019091525f8152606060208201525f610b8f6001808551610b87919061326e565b8591906117f8565b90505f60ff16835f81518110610ba757610ba7613299565b016020015160f81c03610bd05760408051808201909152805f5b81526020019190915292915050565b600160ff16835f81518110610be757610be7613299565b016020015160f81c03610c065760408051808201909152806001610bc1565b600260ff16835f81518110610c1d57610c1d613299565b016020015160f81c03610c3c5760408051808201909152806002610bc1565b60ff8016835f81518110610c5257610c52613299565b016020015160f81c03610c715760408051808201909152806003610bc1565b60405162461bcd60e51b81526020600482015260b360248201527f53757065725478456364736156616c696461746f724c69623a3a20696e76616c60448201527f69642073696720747970652e204578706563746564207072656669782030783060648201527f3020666f72206f66662d636861696e2c203078303120666f72206f6e2d63686160848201527f696e206f72203078303220666f72206572633230207065726d6974206974782060a48201527f68617368207369676e6174757265206f72203078666620666f72206e6f726d6160c48201527f6c20757365724f70207369676e61747572652e0000000000000000000000000060e4820152610104016108a7565b50919050565b5f5f5f5f5f5f87806020019051810190610d989190613430565b945094509450945094505f610dbe8a8565ffffffffffff168565ffffffffffff1661191f565b9050610dcb888784611986565b610dde5760019650505050505050610676565b610de9858783611a0a565b610dfc5760019650505050505050610676565b610e075f8486611a1f565b9a9950505050505050505050565b5f5f610e2084611a55565b90505f610e47868360e0015165ffffffffffff1684610100015165ffffffffffff1661191f565b90505f826040015183606001518460200151604051602001610e8993929190928352602083019190915260f81b6001600160f81b031916604082015260410190565b6040516020818303038152906040529050610ea985846080015183611986565b610eb95760019350505050610676565b610ecc8360c001518460a0015184611a0a565b610edc5760019350505050610676565b6105125f8461010001518560e00151611a1f565b5f5f83806020019051810190610f0691906134d9565b90505f610f2e8683610140015165ffffffffffff1684610160015165ffffffffffff1661191f565b90505f610f3f836101800151611cae565b6101008401516040808601516020808801516080808a015160c0808c01518751958601969096526001600160a01b03808f169786019790975295909216606084015282015260a0810191909152908101829052919250905f9060e0016040516020818303038152906040528051906020012090505f610fc2828760600151611d08565b6101a08701516101c08801516040805160208101939093528201526001600160f81b031960f887901b1660608201529091505f906061016040516020818303038152906040529050611015898383611986565b611029576001975050505050505050610676565b61103e87610120015188610100015188611a0a565b611052576001975050505050505050610676565b8660e00151156111235786516001600160a01b031663d505accf8a61107a60208f018f612f84565b60808b01516101a08c01516101c08d015160405160e087901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b03958616600482015294909316602485015260448401919091526064830189905260ff8a16608484015260a483015260c482015260e4015f604051808303815f87803b15801561110c575f5ffd5b505af115801561111e573d5f5f3e3d5ffd5b505050505b6111385f886101600151896101400151611a1f565b9b9a5050505050505050505050565b5f611153828585611986565b61115f57506001610676565b505f9392505050565b5f81815260018401602090815260408083206001600160a01b0386168452909152812054801561123d575f61119e60018361326e565b90505f60016111ad8888611569565b6111b7919061326e565b9050808214611201575f6111cc888884611d47565b90506111da88888584611d53565b5f90815260018801602090815260408083206001600160a01b038a16845290915290208390555b61120b8787611d5f565b5050505f8281526001808601602090815260408084206001600160a01b03881685529091528220919091559050610676565b5f915050610676565b5f61125085611d69565b156105575761063e848484611da9565b5f308015611319575f5f5f5f5f336001600160a01b03166384b0196e6040518163ffffffff1660e01b81526004015f60405180830381865afa1580156112a8573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526112cf9190810190613601565b506040805186516020978801208183015285519590960194909420606086015260808501929092526001600160a01b031660a084015260c083015260e08201905296505050505050505b6040516002840385013560f01c8060420180860387016119015f52604081602037821582881017896042601e20181715611384575f94507f983e65e5148e570cd828ead231ee759a8d7958721a768f93bc4483ba005c32de85528860205260408520985050506114dd565b7f5479706564446174615369676e280000000000000000000000000000000000008452600e84018360408301823760288185019081525f1901515f1a6029146113fd575f6001858301035b6001820191506029828203515f1a14868310116113cf57508085039450808560408501018337602881830153505b6f07fffffe00000000000001000000000081515f1a1c5b602882515f1a1461143a57806512010000000183515f1a1c179050600182019150611414565b7f20636f6e74656e74732c737472696e67206e616d652c737472696e670000000082527f2076657273696f6e2c75696e7432353620636861696e49642c61646472657373601c8301527f20766572696679696e67436f6e74726163742c627974657333322073616c7429603c830152605c820191508460408401833760408388379084018590038520865260e08620604052600116604201601e20985050909403935b50604052806114f3576114f03386611dcd565b94505b61063e858585611da9565b5f3a610676573a3a526d378edcd5b5b0a24f5342d8c1048560203a3a388461fffffa503a5161155d57604051631626ba7e3a528160205260408052454561ffff0117805a10838814171561154e57fe5b3a3a6064601c3085fa50506040525b50610673848484611da9565b5f8181526020839052604081205461030e565b5f61030e8383611569565b5f610673848484611d47565b5f5f5f5f848060200190518101906115ab91906136d4565b9250925092506115bc878483611986565b6115cb575f9350505050610676565b6115d6828488611a0a565b6115e5575f9350505050610676565b5060019695505050505050565b5f5f6115fd83611a55565b90505f81604001518260600151836020015160405160200161163f93929190928352602083019190915260f81b6001600160f81b031916604082015260410190565b604051602081830303815290604052905061165f86836080015183611986565b61166d575f92505050610676565b6116808260c001518360a0015187611a0a565b61168e575f92505050610676565b50600195945050505050565b5f5f828060200190518101906116b091906134d9565b90505f6116c1826101800151611cae565b90505f8261010001515f1c90505f836040015188856020015186608001518760c0015186604051602001611729969594939291909586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b6040516020818303038152906040528051906020012090505f611750828660600151611d08565b6101a08601516101c08701516040805160208101939093528201526001600160f81b031960f887901b1660608201529091505f9060610160405160208183030381529060405290506117a38a8383611986565b6117b5575f9650505050505050610676565b6117ca8661012001518761010001518b611a0a565b6117dc575f9650505050505050610676565b5060019998505050505050505050565b5f610673848484611986565b60608161180681601f613204565b10156118545760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f7700000000000000000000000000000000000060448201526064016108a7565b61185e8284613204565b845110156118ae5760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e647300000000000000000000000000000060448201526064016108a7565b6060821580156118cc5760405191505f825260208201604052611916565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156119055780518352602092830192016118ed565b5050858452601f01601f1916604052505b50949350505050565b5f61192984611eb8565b60408051602081019290925281018490526060810183905246608082015260a00160408051601f19818403018152828252805160209182012090830152016040516020818303038152906040528051906020012090509392505050565b5f836001600160a01b031661199b8484611ed0565b6001600160a01b0316036119b157506001610676565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f908152601c849052603c90206001600160a01b038516906119f49084611ed0565b6001600160a01b03160361115f57506001610676565b5f82611a168584611ee7565b14949350505050565b5f60d08265ffffffffffff16901b60a08465ffffffffffff16901b85611a45575f611a48565b60015b60ff161717949350505050565b60408051610120810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c082015260e081018290526101008101919091525f825f81518110611aaf57611aaf613299565b016020015160f81c90505f611ae1611ac960066002613742565b60ff168551611ad8919061326e565b859060066117f8565b611aea9061375e565b60d01c90505f611b0d600660ff168651611b04919061326e565b869060066117f8565b611b169061375e565b60d01c90505f856001611b2b60066002613742565b60ff168851611b3a919061326e565b611b44919061326e565b81518110611b5457611b54613299565b016020015160f81c90505f611b6b60066002613742565b60ff16602060ff168360ff16611b8191906131ed565b611b8c906001613204565b611b969190613204565b90505f611bbe600180848b51611bac919061326e565b611bb6919061326e565b8a91906117f8565b90505f611bf1826040805180820182525f8082526020918201528151808301909252825182529182019181019190915290565b90505f611bfd82611f29565b90505f611c0a898361202f565b90506040518061012001604052808a60ff168152602001611c2d835f0151611cae565b60ff1681526020018260200151815260200182604001518152602001611c5e8b87611c57886121aa565b86516121b5565b8152602001611c70836060015161234e565b8152602001611c7f8d896123ee565b81526020018965ffffffffffff1681526020018865ffffffffffff168152509950505050505050505050919050565b5f60258210611cec576023611cc2836124cf565b611ccd9060026131ed565b611cd7908461326e565b611ce1919061326e565b61031190601b613204565b60018211611cff5761031182601b613204565b5090565b919050565b6040517f190100000000000000000000000000000000000000000000000000000000000081526002810182905260228101839052604290205f9061030e565b5f6106738484846124e7565b610aaa848484846124fe565b6103238282612516565b5f6dd9ecebf3c23529de49815dac1c4c6001600160a01b0383161480611d9757506001600160a01b03821633145b80610311575061031160013384610647565b335f90815260208190526040812054610673906001600160a01b0316858585610703565b5f5f5f5f5f866001600160a01b03166384b0196e6040518163ffffffff1660e01b81526004015f60405180830381865afa158015611e0d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611e349190810190613601565b50509450945094509450506040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815284516020860120602082015283516020850120604082015282606082015281608082015260a081209550506719010000000000005f5284601a5285603a52604260182094505f603a525050505092915050565b5f611ec282612552565b805190602001209050919050565b5f80611edc8484612607565b509095945050505050565b5f81815b8451811015611f2157611f1782868381518110611f0a57611f0a613299565b6020026020010151612650565b9150600101611eeb565b509392505050565b6060611f348261267c565b611f3c575f5ffd5b5f611f46836126b3565b90505f8167ffffffffffffffff811115611f6257611f626132ad565b604051908082528060200260200182016040528015611fa657816020015b604080518082019091525f8082526020820152815260200190600190039081611f805790505b5090505f611fb78560200151612736565b8560200151611fc69190613204565b90505f805b8481101561202457611fdc836127af565b915060405180604001604052808381526020018481525084828151811061200557612005613299565b602090810291909101015261201a8284613204565b9250600101611fcb565b509195945050505050565b61205760405180608001604052805f81526020015f81526020015f8152602001606081525090565b5f80808060ff871661207757506005925060069150600790506008612105565b60011960ff88160161209757506007925060099150600a9050600b612105565b60405162461bcd60e51b815260206004820152602860248201527f547856616c696461746f724c69623a3a20756e737570706f727465642065766d60448201527f207478207479706500000000000000000000000000000000000000000000000060648201526084016108a7565b6040518060800160405280612135888660ff168151811061212857612128613299565b6020026020010151612852565b8152602001612152888560ff168151811061212857612128613299565b5f1b8152602001612171888460ff168151811061212857612128613299565b5f1b815260200161219d888760ff168151811061219057612190613299565b602002602001015161289c565b9052979650505050505050565b5f5f6105578361290e565b5f5f6121c083612950565b516121cc906042613204565b90505f8486516121dc919061326e565b90505f6122038283858a516121f1919061326e565b6121fb919061326e565b8991906117f8565b905060011960ff89160161225b578761222a8260405180602001604052805f815250612963565b60405160200161223b9291906137e1565b604051602081830303815290604052805190602001209350505050610557565b60ff88166122e157602585106122c9576122b88161228061227b886124cf565b612950565b6122895f612950565b6122925f612950565b6040516020016122a4939291906137ff565b604051602081830303815290604052612963565b805190602001209350505050610557565b6122b88160405180602001604052805f815250612963565b60405162461bcd60e51b8152602060048201526024808201527f547856616c696461746f724c69623a3a20756e737570706f727465642074782060448201527f747970650000000000000000000000000000000000000000000000000000000060648201526084016108a7565b5f602060ff16825110156123ca5760405162461bcd60e51b815260206004820152602560248201527f54784465636f6465723a3a2063616c6c44617461206c656e67746820746f6f2060448201527f73686f727400000000000000000000000000000000000000000000000000000060648201526084016108a7565b81516123e5906123dc9060209061326e565b839060206117f8565b6103119061381c565b60608160ff1667ffffffffffffffff81111561240c5761240c6132ad565b604051908082528060200260200182016040528015612435578160200160208202803683370190505b5090505f600161244760066002613742565b60ff168551612456919061326e565b612460919061326e565b90505b60ff8316156124c85761248361247a60208361326e565b859060206117f8565b61248c9061381c565b8261249860018661383f565b60ff16815181106124ab576124ab613299565b6020908102919091010152826124c081613858565b935050612463565b5092915050565b5f60026124dd60238461326e565b61031191906131da565b5f82815260208490526040812061067390836129c3565b5f838152602085905260409020610aaa9083836129e9565b5f818152602083905260408120805490918190036125345750505050565b6125498261254360018461326e565b5f6129e9565b5f190190555050565b6060813560208301355f61257161256c6040870187613217565b612a12565b90505f61258461256c6060880188613217565b9050608086013560a087013560c08801355f6125a661256c60e08c018c613217565b604080516001600160a01b039a909a1660208b015289810198909852606089019690965250608087019390935260a086019190915260c085015260e08401526101008084019190915281518084039091018152610120909201905292915050565b5f5f5f835160410361263e576020840151604085015160608601515f1a61263088828585612a24565b955095509550505050612649565b505081515f91506002905b9250925092565b5f81831061266a575f82815260208490526040902061030e565b5f83815260208390526040902061030e565b80515f90810361268d57505f919050565b602082015180515f1a9060c08210156126a957505f9392505050565b5060019392505050565b80515f9081036126c457505f919050565b5f5f90505f6126d68460200151612736565b84602001516126e59190613204565b90505f845f015185602001516126fb9190613204565b90505b8082101561272d5761270f826127af565b6127199083613204565b91508261272581613281565b9350506126fe565b50909392505050565b80515f90811a608081101561274d57505f92915050565b60b8811080612768575060c08110801590612768575060f881105b156127765750600192915050565b60c08110156127a35761278b600160b861383f565b6127989060ff168261326e565b610676906001613204565b61278b600160f861383f565b80515f908190811a60808110156127c957600191506124c8565b60b88110156127ef576127dd60808261326e565b6127e8906001613204565b91506124c8565b60c081101561281c5760b78103600185019450806020036101000a855104600182018101935050506124c8565b60f8811015612830576127dd60c08261326e565b60019390930151602084900360f7016101000a900490920160f5190192915050565b80515f901580159061286657508151602110155b61286e575f5ffd5b5f5f6128798461290e565b8151919350915060208210156105575760208290036101000a9004949350505050565b80516060906128a9575f5ffd5b5f5f6128b48461290e565b915091505f8167ffffffffffffffff8111156128d2576128d26132ad565b6040519080825280601f01601f1916602001820160405280156128fc576020820181803683370190505b50905060208101611916848285612aec565b5f5f5f61291e8460200151612736565b90505f8185602001516129319190613204565b90505f82865f0151612943919061326e565b9196919550909350505050565b606061031161295e83612b6c565b612c84565b60605f8383604051602001612979929190613873565b60408051601f1981840301815291905280519091506129999060c0612cd3565b816040516020016129ab929190613873565b60405160208183030381529060405291505092915050565b5f825482106129dd57638277484f5f52816020526024601cfd5b50600101602002015490565b82548210612a0257638277484f5f52816020526024601cfd5b8060018301602002840155505050565b5f604051828085833790209392505050565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115612a5d57505f91506003905082612ae2565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015612aae573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b038116612ad957505f925060019150829050612ae2565b92505f91508190505b9450945094915050565b805f03612af857505050565b60208110612b305782518252612b0f602084613204565b9250612b1c602083613204565b9150612b2960208261326e565b9050612af8565b8015610475575f6001612b4483602061326e565b612b5090610100613964565b612b5a919061326e565b84518451821691191617835250505050565b6040805160208082528183019092526060915f91906020820181803683375050506020810184905290505f5b6020811015612bd057818181518110612bb357612bb3613299565b01602001516001600160f81b0319165f03612bd057600101612b98565b5f612bdc82602061326e565b67ffffffffffffffff811115612bf457612bf46132ad565b6040519080825280601f01601f191660200182016040528015612c1e576020820181803683370190505b5090505f5b8151811015611916578383612c3781613281565b945081518110612c4957612c49613299565b602001015160f81c60f81b828281518110612c6657612c66613299565b60200101906001600160f81b03191690815f1a905350600101612c23565b60608082516001148015612cb157506080835f81518110612ca757612ca7613299565b016020015160f81c105b15612cbd575081610311565b61030e83612ccd85516080612cd3565b90612e84565b6060806038841015612d3b5760408051600180825281830190925290602082018180368337019050509050612d088385613204565b601f1a60f81b815f81518110612d2057612d20613299565b60200101906001600160f81b03191690815f1a90535061030e565b5f60015b612d4981876131da565b15612d6f5781612d5881613281565b9250612d689050610100826131ed565b9050612d3f565b612d7a826001613204565b67ffffffffffffffff811115612d9257612d926132ad565b6040519080825280601f01601f191660200182016040528015612dbc576020820181803683370190505b509250612dc98583613204565b612dd4906037613204565b601f1a60f81b835f81518110612dec57612dec613299565b60200101906001600160f81b03191690815f1a905350600190505b818111612e7b57610100612e1b828461326e565b612e2790610100613964565b612e3190886131da565b612e3b919061396f565b601f1a60f81b838281518110612e5357612e53613299565b60200101906001600160f81b03191690815f1a90535080612e7381613281565b915050612e07565b50509392505050565b6060806040519050835180825260208201818101602087015b81831015612eb5578051835260209283019201612e9d565b50855184518101855292509050808201602086015b81831015612ee2578051835260209283019201612eca565b508651929092011591909101601f01601f191660405250905092915050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b6001600160a01b0381168114612f4a575f5ffd5b50565b5f5f60408385031215612f5e575f5ffd5b8235612f6981612f36565b91506020830135612f7981612f36565b809150509250929050565b5f60208284031215612f94575f5ffd5b813561030e81612f36565b5f5f83601f840112612faf575f5ffd5b50813567ffffffffffffffff811115612fc6575f5ffd5b6020830191508360208285010111156108ee575f5ffd5b5f5f60208385031215612fee575f5ffd5b823567ffffffffffffffff811115613004575f5ffd5b61301085828601612f9f565b90969095509350505050565b5f5f5f5f5f60608688031215613030575f5ffd5b85359450602086013567ffffffffffffffff81111561304d575f5ffd5b61305988828901612f9f565b909550935050604086013567ffffffffffffffff811115613078575f5ffd5b61308488828901612f9f565b969995985093965092949392505050565b5f5f604083850312156130a6575f5ffd5b823567ffffffffffffffff8111156130bc575f5ffd5b830161012081860312156130ce575f5ffd5b946020939093013593505050565b5f602082840312156130ec575f5ffd5b5035919050565b5f5f5f5f60608587031215613106575f5ffd5b843561311181612f36565b935060208501359250604085013567ffffffffffffffff811115613133575f5ffd5b61313f87828801612f9f565b95989497509550505050565b5f5f85851115613159575f5ffd5b83861115613165575f5ffd5b5050820193919092039150565b80356bffffffffffffffffffffffff1981169060148410156124c8576bffffffffffffffffffffffff1960149490940360031b84901b1690921692915050565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f826131e8576131e86131b2565b500490565b8082028115828204841417610311576103116131c6565b80820180821115610311576103116131c6565b5f5f8335601e1984360301811261322c575f5ffd5b83018035915067ffffffffffffffff821115613246575f5ffd5b6020019150368190038213156108ee575f5ffd5b634e487b7160e01b5f52602160045260245ffd5b81810381811115610311576103116131c6565b5f60018201613292576132926131c6565b5060010190565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e0810167ffffffffffffffff811182821017156132e5576132e56132ad565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613314576133146132ad565b604052919050565b5f67ffffffffffffffff821115613335576133356132ad565b5060051b60200190565b5f82601f83011261334e575f5ffd5b815161336161335c8261331c565b6132eb565b8082825260208201915060208360051b860101925085831115613382575f5ffd5b602085015b8381101561339f578051835260209283019201613387565b5095945050505050565b805165ffffffffffff81168114611d03575f5ffd5b5f82601f8301126133cd575f5ffd5b8151602083015f5f67ffffffffffffffff8411156133ed576133ed6132ad565b50601f8301601f1916602001613402816132eb565b915050828152858383011115613416575f5ffd5b8282602083015e5f92810160200192909252509392505050565b5f5f5f5f5f60a08688031215613444575f5ffd5b8551602087015190955067ffffffffffffffff811115613462575f5ffd5b61346e8882890161333f565b94505061347d604087016133a9565b925061348b606087016133a9565b9150608086015167ffffffffffffffff8111156134a6575f5ffd5b6134b2888289016133be565b9150509295509295909350565b8051611d0381612f36565b80518015158114611d03575f5ffd5b5f602082840312156134e9575f5ffd5b815167ffffffffffffffff8111156134ff575f5ffd5b82016101e08185031215613511575f5ffd5b6135196132c1565b613522826134bf565b8152613530602083016134bf565b602082015260408281015190820152606080830151908201526080808301519082015260a0808301519082015260c0808301519082015261357360e083016134ca565b60e0820152610100828101519082015261012082015167ffffffffffffffff81111561359d575f5ffd5b6135a98682850161333f565b610120830152506135bd61014083016133a9565b6101408201526135d061016083016133a9565b61016082015261018082810151908201526101a080830151908201526101c091820151918101919091529392505050565b5f5f5f5f5f5f5f60e0888a031215613617575f5ffd5b87516001600160f81b03198116811461362e575f5ffd5b602089015190975067ffffffffffffffff81111561364a575f5ffd5b6136568a828b016133be565b965050604088015167ffffffffffffffff811115613672575f5ffd5b61367e8a828b016133be565b60608a015190965094506136969050608089016134bf565b60a089015160c08a0151919450925067ffffffffffffffff8111156136b9575f5ffd5b6136c58a828b0161333f565b91505092959891949750929550565b5f5f5f606084860312156136e6575f5ffd5b8351602085015190935067ffffffffffffffff811115613704575f5ffd5b6137108682870161333f565b925050604084015167ffffffffffffffff81111561372c575f5ffd5b613738868287016133be565b9150509250925092565b60ff81811683821602908116908181146124c8576124c86131c6565b805160208201517fffffffffffff00000000000000000000000000000000000000000000000000008116919060068210156137c3577fffffffffffff0000000000000000000000000000000000000000000000000000808360060360031b1b82161692505b5050919050565b5f81518060208401855e5f93019283525090919050565b6001600160f81b03198360f81b1681525f61067360018301846137ca565b5f61063e61381661381084886137ca565b866137ca565b846137ca565b80516020808301519190811015610d78575f1960209190910360031b1b16919050565b60ff8281168282160390811115610311576103116131c6565b5f60ff82168061386a5761386a6131c6565b5f190192915050565b5f61067361381683866137ca565b6001815b60018411156138bc578085048111156138a0576138a06131c6565b60018416156138ae57908102905b60019390931c928002613885565b935093915050565b5f826138d257506001610311565b816138de57505f610311565b81600181146138f457600281146138fe5761391a565b6001915050610311565b60ff84111561390f5761390f6131c6565b50506001821b610311565b5060208310610133831016604e8410600b841016171561393d575081810a610311565b6139495f198484613881565b805f190482111561395c5761395c6131c6565b029392505050565b5f61030e83836138c4565b5f8261397d5761397d6131b2565b50069056fea164736f6c634300081b000a
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.