Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 16895271 | 2 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
Nexus
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 999 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { BaseAccount } from "./base/BaseAccount.sol"; import { ModuleManager } from "./base/ModuleManager.sol"; import { ExecutionHelper } from "./base/ExecutionHelper.sol"; import { ComposableExecutionBase, ComposableExecution } from "../composability/ComposableExecutionBase.sol"; import { Initializable } from "../lib/nexus/Initializable.sol"; import { UUPSUpgradeable } from "solady/utils/UUPSUpgradeable.sol"; import { INexus } from "../interfaces/nexus/INexus.sol"; import { ExecLib } from "../lib/erc-7579/ExecLib.sol"; import { NonceLib } from "../lib/nexus/NonceLib.sol"; import { SentinelListLib, SENTINEL, ZERO_ADDRESS } from "sentinellist/SentinelList.sol"; import { EmergencyUninstall } from "../types/DataTypes.sol"; import { LibPREP } from "../lib/nexus/local/LibPREP.sol"; import { ECDSA } from "solady/utils/ECDSA.sol"; import { IValidator } from "erc7579/interfaces/IERC7579Module.sol"; import { MODULE_TYPE_VALIDATOR, MODULE_TYPE_EXECUTOR, MODULE_TYPE_FALLBACK, MODULE_TYPE_HOOK, SUPPORTS_ERC7739 } from "../types/Constants.sol"; import { ModeLib, ExecutionMode, ExecType, CallType, CALLTYPE_BATCH, CALLTYPE_SINGLE, CALLTYPE_DELEGATECALL, EXECTYPE_DEFAULT, EXECTYPE_TRY } from "../lib/erc-7579/ModeLib.sol"; import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; /// @title Nexus - Smart Account /// @notice This contract integrates various functionalities to handle modular smart accounts compliant with ERC-7579 /// and /// ERC-4337 standards. /// @dev Comprehensive suite of methods for managing smart accounts, integrating module management, execution /// management, /// and upgradability via UUPS. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgradeable, ComposableExecutionBase { using ModeLib for ExecutionMode; using ExecLib for bytes; using NonceLib for uint256; using SentinelListLib for SentinelListLib.SentinelList; /// @dev The timelock period for emergency hook uninstallation. uint256 internal constant _EMERGENCY_TIMELOCK = 1 days; /// @dev The event emitted when an emergency hook uninstallation is initiated. event EmergencyHookUninstallRequest(address hook, uint256 timestamp); /// @dev The event emitted when an emergency hook uninstallation request is reset. event EmergencyHookUninstallRequestReset(address hook, uint256 timestamp); /// @notice Initializes the smart account with the specified entry point. constructor( address anEntryPoint, address defaultValidator, bytes memory initData ) ModuleManager(defaultValidator, initData) { require(address(anEntryPoint) != address(0), EntryPointCanNotBeZero()); _ENTRYPOINT = anEntryPoint; } /// @notice Validates a user operation against a specified validator, extracted from the operation's nonce. /// @param op The user operation to validate, encapsulating all transaction details. /// @param userOpHash Hash of the user operation data, used for signature validation. /// @param missingAccountFunds Funds missing from the account's deposit necessary for transaction execution. /// This can be zero if covered by a paymaster or if sufficient deposit exists. /// @return validationData Encoded validation result or failure, propagated from the validator module. /// - Encoded format in validationData: /// - First 20 bytes: Address of the Validator module, to which the validation task is forwarded. /// The validator module returns: /// - `SIG_VALIDATION_SUCCESS` (0) indicates successful validation. /// - `SIG_VALIDATION_FAILED` (1) indicates signature validation failure. /// @dev Expects the validator's address to be encoded in the upper 96 bits of the user operation's nonce. /// This method forwards the validation task to the extracted validator module address. /// @dev The entryPoint calls this function. If validation fails, it returns `VALIDATION_FAILED` (1) otherwise `0`. /// @dev Features Module Enable Mode. /// This Module Enable Mode flow is intended for the module acting as the validator /// for the user operation that triggers the Module Enable Flow. Otherwise, a call to /// `Nexus.installModule` should be included in `userOp.callData`. function validateUserOp( PackedUserOperation calldata op, bytes32 userOpHash, uint256 missingAccountFunds ) external virtual payPrefund(missingAccountFunds) onlyEntryPoint returns (uint256 validationData) { address validator; PackedUserOperation memory userOp = op; if (op.nonce.isValidateMode()) { // do nothing special. This is introduced // to quickly identify the most commonly used // mode which is validate mode // and avoid checking two above conditions } else if (op.nonce.isModuleEnableMode()) { // if it is module enable mode, we need to enable the module first // and get the cleaned signature userOp.signature = _enableMode(userOpHash, op.signature); } else if (op.nonce.isPrepMode()) { // PREP Mode. Authorize prep signature // and initialize the account // PREP mode is only used for the uninited PREPs require(!isInitialized(), AccountAlreadyInitialized()); bytes calldata initData; (userOp.signature, initData) = _handlePREP(op.signature); _initializeAccount(initData); } validator = _handleValidator(op.nonce.getValidator()); (userOpHash, userOp.signature) = _withPreValidationHook(userOpHash, userOp, missingAccountFunds); validationData = IValidator(validator).validateUserOp(userOp, userOpHash); } /// @notice Executes transactions in single or batch modes as specified by the execution mode. /// @param mode The execution mode detailing how transactions should be handled (single, batch, default, try/catch). /// @param executionCalldata The encoded transaction data to execute. /// @dev This function handles transaction execution flexibility and is protected by the `onlyEntryPoint` modifier. /// @dev This function also goes through hook checks via withHook modifier. function execute(ExecutionMode mode, bytes calldata executionCalldata) external payable onlyEntryPoint withHook { (CallType callType, ExecType execType) = mode.decodeBasic(); if (callType == CALLTYPE_SINGLE) { _handleSingleExecution(executionCalldata, execType); } else if (callType == CALLTYPE_BATCH) { _handleBatchExecution(executionCalldata, execType); } else if (callType == CALLTYPE_DELEGATECALL) { _handleDelegateCallExecution(executionCalldata, execType); } else { revert UnsupportedCallType(callType); } } /// @notice Executes transactions from an executor module, supporting both single and batch transactions. /// @param mode The execution mode (single or batch, default or try). /// @param executionCalldata The transaction data to execute. /// @return returnData The results of the transaction executions, which may include errors in try mode. /// @dev This function is callable only by an executor module and goes through hook checks. function executeFromExecutor( ExecutionMode mode, bytes calldata executionCalldata ) external payable onlyExecutorModule withHook returns (bytes[] memory returnData) { (CallType callType, ExecType execType) = mode.decodeBasic(); // check if calltype is batch or single or delegate call if (callType == CALLTYPE_SINGLE) { returnData = _handleSingleExecutionAndReturnData(executionCalldata, execType); } else if (callType == CALLTYPE_BATCH) { returnData = _handleBatchExecutionAndReturnData(executionCalldata, execType); } else if (callType == CALLTYPE_DELEGATECALL) { returnData = _handleDelegateCallExecutionAndReturnData(executionCalldata, execType); } else { revert UnsupportedCallType(callType); } } /// @notice Executes a user operation via a call using the contract's context. /// @param userOp The user operation to execute, containing transaction details. /// @param - Hash of the user operation. /// @dev Only callable by the EntryPoint. Decodes the user operation calldata, skipping the first four bytes, and /// executes the inner call. function executeUserOp( PackedUserOperation calldata userOp, bytes32 ) external payable virtual onlyEntryPoint withHook { bytes calldata callData = userOp.callData[4:]; (bool success,) = address(this).delegatecall(callData); assembly { if iszero(success) { // revert ExecutionFailed() mstore(0x00, 0xacfdb444) revert(0x1c, 0x04) } } } /// @notice Executes a composable execution /// See more about composability here: https://docs.biconomy.io/composability /// @param executions The composable executions to execute function executeComposable(ComposableExecution[] calldata executions) external payable override onlyEntryPoint withHook { _executeComposable(executions); } /// @notice Executes a call to a target address with specified value and data. /// @param to The address to execute the action on /// @param value The value to send with the action /// @param data The data to send with the action /// @return result The result of the execution function _executeAction(address to, uint256 value, bytes memory data) internal override returns (bytes memory) { return _executeMemory(to, value, data); } /// @notice Installs a new module to the smart account. /// @param moduleTypeId The type identifier of the module being installed, which determines its role: /// - 1 for Validator /// - 2 for Executor /// - 3 for Fallback /// - 4 for Hook /// - 8 for 1271 Prevalidation Hook /// - 9 for 4337 Prevalidation Hook /// @param module The address of the module to install. /// @param initData Initialization data for the module. /// @dev This function can only be called by the EntryPoint or the account itself for security reasons. /// @dev This function goes through hook checks via withHook modifier through internal function _installModule. function installModule( uint256 moduleTypeId, address module, bytes calldata initData ) external payable virtual override onlyEntryPointOrSelf { _installModule(moduleTypeId, module, initData); assembly { // emit ModuleInstalled(moduleTypeId, module) mstore(0x00, moduleTypeId) mstore(0x20, module) log1(0x00, 0x40, 0xd21d0b289f126c4b473ea641963e766833c2f13866e4ff480abd787c100ef123) } } /// @notice Uninstalls a module from the smart account. /// @param moduleTypeId The type ID of the module to be uninstalled, matching the installation type: /// - 1 for Validator /// - 2 for Executor /// - 3 for Fallback /// - 4 for Hook /// - 8 for 1271 Prevalidation Hook /// - 9 for 4337 Prevalidation Hook /// @dev Attention: All the underlying functions _uninstall[ModuleType] are calling module.onInstall() method. /// If the module is malicious (which is not likely because such a module won't be attested), it can prevent /// itself from being uninstalled by spending all gas in the onUninstall() method. Then 1/64 gas left can /// be not enough to finish the uninstallation, assuming there may be hook postCheck() call. /// In this highly unlikely scenario, user will have to uninstall the hook, then uninstall the malicious /// module => in this case 1/64 gas left should be enough to finish the uninstallation. /// @param module The address of the module to uninstall. /// @param deInitData De-initialization data for the module. /// @dev Ensures that the operation is authorized and valid before proceeding with the uninstallation. function uninstallModule( uint256 moduleTypeId, address module, bytes calldata deInitData ) external payable onlyEntryPointOrSelf withHook { require(_isModuleInstalled(moduleTypeId, module, deInitData), ModuleNotInstalled(moduleTypeId, module)); if (moduleTypeId == MODULE_TYPE_VALIDATOR) { _uninstallValidator(module, deInitData); _checkInitializedValidators(); } else if (moduleTypeId == MODULE_TYPE_EXECUTOR) { _uninstallExecutor(module, deInitData); } else if (moduleTypeId == MODULE_TYPE_FALLBACK) { _uninstallFallbackHandler(module, deInitData); } else if (moduleTypeId == MODULE_TYPE_HOOK || _isPrevalidationHookType(moduleTypeId)) { _uninstallHook(module, moduleTypeId, deInitData); } emit ModuleUninstalled(moduleTypeId, module); } function emergencyUninstallHook(EmergencyUninstall calldata data, bytes calldata signature) external payable { // Validate the signature _checkEmergencyUninstallSignature(data, signature); // Parse uninstall data (uint256 hookType, address hook, bytes calldata deInitData) = (data.hookType, data.hook, data.deInitData); // Validate the hook is of a supported type and is installed require(hookType == MODULE_TYPE_HOOK || _isPrevalidationHookType(hookType), UnsupportedModuleType(hookType)); require(_isModuleInstalled(hookType, hook, deInitData), ModuleNotInstalled(hookType, hook)); // Get the account storage AccountStorage storage accountStorage = _getAccountStorage(); uint256 hookTimelock = accountStorage.emergencyUninstallTimelock[hook]; if (hookTimelock == 0) { // if the timelock hasnt been initiated, initiate it accountStorage.emergencyUninstallTimelock[hook] = block.timestamp; emit EmergencyHookUninstallRequest(hook, block.timestamp); } else if (block.timestamp >= hookTimelock + 3 * _EMERGENCY_TIMELOCK) { // if the timelock has been left for too long, reset it accountStorage.emergencyUninstallTimelock[hook] = block.timestamp; emit EmergencyHookUninstallRequestReset(hook, block.timestamp); } else if (block.timestamp >= hookTimelock + _EMERGENCY_TIMELOCK) { // if the timelock expired, clear it and uninstall the hook accountStorage.emergencyUninstallTimelock[hook] = 0; _uninstallHook(hook, hookType, deInitData); emit ModuleUninstalled(hookType, hook); } else { // if the timelock is initiated but not expired, revert revert EmergencyTimeLockNotExpired(); } } /// @notice Initializes the smart account with the specified initialization data. /// @param initData The initialization data for the smart account. /// @dev This function can only be called by the account itself or the proxy factory. /// When a 7702 account is created, the first userOp should contain self-call to initialize the account. function initializeAccount(bytes calldata initData) external payable virtual { // Protect this function to only be callable when used with the proxy factory or when // account calls itself if (msg.sender != address(this)) { if (_amIERC7702()) { // If this is a 7702 account, we allow passing a user signature with the initData // to initialize the account. This allows 7702 accounts to be initialized // by a relayer. bytes calldata signature = initData[0:65]; AccountStorage storage $accountStorage = _getAccountStorage(); // Remove the signature from the initData initData = initData[65:]; // Calculate the hash of the initData bytes32 initDataHash; assembly { let ptr := mload(0x40) calldatacopy(ptr, initData.offset, initData.length) initDataHash := keccak256(ptr, initData.length) } // Make sure the account has not been already initialized // Means relay can not re-initialize the account require(!isInitialized(), AccountAlreadyInitialized()); // Make sure the initHash is not already used require(!$accountStorage.erc7702InitHashes[initDataHash], AccountAlreadyInitialized()); // Check if the signature is valid require(ECDSA.recover(initDataHash, signature) == address(this), InvalidSignature()); // Mark the initDataHash as used $accountStorage.erc7702InitHashes[initDataHash] = true; } else { Initializable.requireInitializable(); } } _initializeAccount(initData); } function _initializeAccount(bytes calldata initData) internal { require(initData.length >= 24, InvalidInitData()); address bootstrap; bytes calldata bootstrapCall; assembly { bootstrap := calldataload(initData.offset) let s := calldataload(add(initData.offset, 0x20)) let u := add(initData.offset, s) bootstrapCall.offset := add(u, 0x20) bootstrapCall.length := calldataload(u) } (bool success,) = bootstrap.delegatecall(bootstrapCall); assembly { if iszero(success) { mstore(0x00, 0x315927c5) // NexusInitializationFailed() revert(0x1c, 0x04) } } if (!_amIERC7702()) { require(isInitialized(), AccountNotInitialized()); } } /// @notice Validates a signature according to ERC-1271 standards. /// @param hash The hash of the data being validated. /// @param signature Signature data that needs to be validated. /// @return The status code of the signature validation (`0x1626ba7e` if valid). /// bytes4(keccak256("isValidSignature(bytes32,bytes)") = 0x1626ba7e /// @dev Delegates the validation to a validator module specified within the signature data. function isValidSignature(bytes32 hash, bytes calldata signature) external view virtual override returns (bytes4) { // Handle potential ERC7739 support detection request if (signature.length == 0) { // Forces the compiler to optimize for smaller bytecode size. /// forge-lint:disable-next-line(divide-before-multiply) if (uint256(hash) == (~signature.length / 0xffff) * 0x7739) { return _checkERC7739Support(hash, signature); } } // else proceed with normal signature verification // First 20 bytes of data will be validator address and rest of the bytes is complete signature. address validator = _handleValidator(address(bytes20(signature[0:20]))); bytes memory signature_; (hash, signature_) = _withPreValidationHook(hash, signature[20:]); try IValidator(validator).isValidSignatureWithSender(msg.sender, hash, signature_) returns (bytes4 res) { return res; } catch { return bytes4(0xffffffff); } } /// @notice Retrieves the address of the current implementation from the EIP-1967 slot. /// @notice Checks the 1967 implementation slot, if not found then checks the slot defined by address (Biconomy V2 /// smart account) /// @return implementation The address of the current contract implementation. function getImplementation() external view returns (address implementation) { assembly { implementation := sload(_ERC1967_IMPLEMENTATION_SLOT) } if (implementation == address(0)) { assembly { implementation := sload(address()) } } } /// @notice Checks if a specific module type is supported by this smart account. /// @param moduleTypeId The identifier of the module type to check. /// @return True if the module type is supported, false otherwise. function supportsModule(uint256 moduleTypeId) external view virtual returns (bool) { /* MODULE_TYPE_VALIDATOR || MODULE_TYPE_EXECUTOR || MODULE_TYPE_FALLBACK || MODULE_TYPE_HOOK || MODULE_TYPE_PREVALIDATION_HOOK_ERC1271 || MODULE_TYPE_PREVALIDATION_HOOK_ERC4337 || MODULE_TYPE_MULTI */ // Bitmap: 0x31F represents supported module types: 0,1,2,3,4,8,9 // 0x31F = 0b1100011111 = (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<8)|(1<<9) /// forge-lint:disable-next-line(incorrect-shift) return ((1 << moduleTypeId) & 0x31F) != 0; } /// @notice Determines if a specific execution mode is supported. /// @param mode The execution mode to evaluate. /// @return isSupported True if the execution mode is supported, false otherwise. function supportsExecutionMode(ExecutionMode mode) external view virtual returns (bool isSupported) { (CallType callType, ExecType execType) = mode.decodeBasic(); // Return true if both the call type and execution type are supported. return (callType == CALLTYPE_SINGLE || callType == CALLTYPE_BATCH || callType == CALLTYPE_DELEGATECALL) && (execType == EXECTYPE_DEFAULT || execType == EXECTYPE_TRY); } /// @notice Determines whether a module is installed on the smart account. /// @param moduleTypeId The ID corresponding to the type of module (Validator, Executor, Fallback, Hook). /// @param module The address of the module to check. /// @param additionalContext Optional context that may be needed for certain checks. /// @return True if the module is installed, false otherwise. function isModuleInstalled( uint256 moduleTypeId, address module, bytes calldata additionalContext ) external view returns (bool) { return _isModuleInstalled(moduleTypeId, module, additionalContext); } /// @notice Checks if the smart account is initialized. /// @return True if the smart account is initialized, false otherwise. /// @dev In case default validator is initialized, two other SLOADS from _areSentinelListsInitialized() are not /// checked, /// this method should not introduce huge gas overhead. function isInitialized() public view returns (bool) { return (IValidator(_DEFAULT_VALIDATOR).isInitialized(address(this)) || _areSentinelListsInitialized()); } /// Returns the account's implementation ID. /// @return The unique identifier for this account implementation. function accountId() external pure virtual returns (string memory) { return "biconomy.nexus.1.3.1"; } /// Upgrades the contract to a new implementation and calls a function on the new contract. /// @notice Updates two slots 1. ERC1967 slot and /// 2. address() slot in case if it's potentially upgraded earlier from Biconomy V2 account, /// as Biconomy v2 Account (proxy) reads implementation from the slot that is defined by its address /// @param newImplementation The address of the new contract implementation. /// @param data The calldata to be sent to the new implementation. function upgradeToAndCall(address newImplementation, bytes calldata data) public payable virtual override withHook { require(newImplementation != address(0), InvalidImplementationAddress()); bool res; assembly { res := gt(extcodesize(newImplementation), 0) } require(res, InvalidImplementationAddress()); // update the address() storage slot as well // This is needed in case the og proxy is Nexus v2 proxy which // reads the implementation from the slot defined by its address assembly { sstore(address(), newImplementation) } UUPSUpgradeable.upgradeToAndCall(newImplementation, data); } /// @dev For automatic detection that the smart account supports the ERC7739 workflow /// Iterates over all the validators but only if this is a detection request /// ERC-7739 spec assumes that if the account doesn't support ERC-7739 /// it will try to handle the detection request as it was normal sig verification /// request and will return 0xffffffff since it won't be able to verify the 0x signature /// against 0x7739...7739 hash. /// So this approach is consistent with the ERC-7739 spec. /// If no validator supports ERC-7739, this function returns false /// thus the account will proceed with normal signature verification /// and return 0xffffffff as a result. function _checkERC7739Support(bytes32 hash, bytes calldata signature) internal view virtual returns (bytes4) { bytes4 result; unchecked { SentinelListLib.SentinelList storage validators = _getAccountStorage().validators; address next = validators.entries[SENTINEL]; while (next != ZERO_ADDRESS && next != SENTINEL) { result = _get7739Version(next, result, hash, signature); next = validators.getNext(next); } } result = _get7739Version(_DEFAULT_VALIDATOR, result, hash, signature); // check default validator return result == bytes4(0) ? bytes4(0xffffffff) : result; } function _get7739Version( address validator, bytes4 prevResult, bytes32 hash, bytes calldata signature ) internal view returns (bytes4) { bytes4 support = IValidator(validator).isValidSignatureWithSender(msg.sender, hash, signature); /// forge-lint:disable-next-line(unsafe-typecast) if (bytes2(support) == bytes2(SUPPORTS_ERC7739) && support > prevResult) { return support; } return prevResult; } /// @dev Ensures that only authorized callers can upgrade the smart contract implementation. /// This is part of the UUPS (Universal Upgradeable Proxy Standard) pattern. /// param newImplementation The address of the new implementation to upgrade to. function _authorizeUpgrade( address /* newImplementation */ ) internal virtual override(UUPSUpgradeable) onlyEntryPointOrSelf { if (_amIERC7702()) { revert ERC7702AccountCannotBeUpgradedThisWay(); } } /// @dev Handles the PREP initialization. /// @param data The packed data to be handled. /// @return cleanedSignature The cleaned signature for Nexus 4337 (validateUserOp) flow. /// @return initData The data to initialize the account with. function _handlePREP(bytes calldata data) internal returns (bytes calldata cleanedSignature, bytes calldata initData) { bytes32 saltAndDelegation; // unpack the data assembly { if lt(data.length, 0xf9) { mstore(0x0, 0xaed59595) // NotInitializable() revert(0x1c, 0x04) } saltAndDelegation := calldataload(data.offset) // initData let p := calldataload(add(data.offset, 0x20)) let u := add(data.offset, p) initData.offset := add(u, 0x20) initData.length := calldataload(u) // cleanedSignature p := calldataload(add(data.offset, 0x40)) u := add(data.offset, p) cleanedSignature.offset := add(u, 0x20) cleanedSignature.length := calldataload(u) } // check r is valid bytes32 r = LibPREP.rPREP(address(this), keccak256(initData), saltAndDelegation); assembly { if iszero(r) { mstore(0x00, 0xe483bbcb) // revert InvalidPREP() revert(0x1c, 0x04) } // emit PREPInitialized(r) mstore(0x00, r) log1(0x00, 0x20, 0x4f058962bce244bca6c9be42f256083afc66f1f63a1f9a04e31a3042311af38d) } } // checks if there's at least one validator initialized function _checkInitializedValidators() internal view { if (!_amIERC7702() && !IValidator(_DEFAULT_VALIDATOR).isInitialized(address(this))) { unchecked { SentinelListLib.SentinelList storage validators = _getAccountStorage().validators; address next = validators.entries[SENTINEL]; while (next != ZERO_ADDRESS && next != SENTINEL) { if (IValidator(next).isInitialized(address(this))) { break; } next = validators.getNext(next); } if (next == SENTINEL) { //went through all validators and none was initialized assembly { // revert CanNotRemoveLastValidator() mstore(0x00, 0xcc319d84) revert(0x1c, 0x04) } } } } } /// @dev EIP712 domain name and version. function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) { name = "Nexus"; version = "1.3.1"; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IBaseAccount } from "../../interfaces/nexus/base/IBaseAccount.sol"; /// @title Nexus - BaseAccount /// @notice Implements ERC-4337 and ERC-7579 standards for account management and access control within the Nexus suite. /// @dev Manages entry points and configurations as specified in the ERC-4337 and ERC-7579 documentation. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady contract BaseAccount is IBaseAccount { /// @notice The canonical address for the ERC4337 EntryPoint contract, version 0.7. /// This address is consistent across all supported networks. address internal immutable _ENTRYPOINT; /// @dev Ensures the caller is either the EntryPoint or this account itself. /// Reverts with AccountAccessUnauthorized if the check fails. modifier onlyEntryPointOrSelf() { require(msg.sender == _ENTRYPOINT || msg.sender == address(this), AccountAccessUnauthorized()); _; } /// @dev Ensures the caller is the EntryPoint. /// Reverts with AccountAccessUnauthorized if the check fails. modifier onlyEntryPoint() { require(msg.sender == _ENTRYPOINT, AccountAccessUnauthorized()); _; } /// @dev Sends to the EntryPoint (i.e. `msg.sender`) the missing funds for this transaction. /// Subclass MAY override this modifier for better funds management. /// (e.g. send to the EntryPoint more than the minimum required, so that in future transactions /// it will not be required to send again) /// /// `missingAccountFunds` is the minimum value this modifier should send the EntryPoint, /// which MAY be zero, in case there is enough deposit, or the userOp has a paymaster. modifier payPrefund(uint256 missingAccountFunds) virtual { _; /// @solidity memory-safe-assembly assembly { if missingAccountFunds { // Ignore failure (it's EntryPoint's job to verify, not the account's). pop(call(gas(), caller(), missingAccountFunds, codesize(), 0x00, codesize(), 0x00)) } } } /// @notice Adds deposit to the EntryPoint to fund transactions. function addDeposit() external payable virtual { address entryPointAddress = _ENTRYPOINT; /// @solidity memory-safe-assembly assembly { // The EntryPoint has balance accounting logic in the `receive()` function. if iszero(call(gas(), entryPointAddress, callvalue(), codesize(), 0x00, codesize(), 0x00)) { revert(codesize(), 0x00) } // For gas estimation. } } /// @notice Withdraws ETH from the EntryPoint to a specified address. /// @param to The address to receive the withdrawn funds. /// @param amount The amount to withdraw. function withdrawDepositTo(address to, uint256 amount) external payable virtual onlyEntryPointOrSelf { address entryPointAddress = _ENTRYPOINT; assembly { let freeMemPtr := mload(0x40) // Store the free memory pointer. mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0x205c2878000000000000000000000000) // `withdrawTo(address,uint256)`. if iszero(call(gas(), entryPointAddress, 0, 0x10, 0x44, codesize(), 0x00)) { returndatacopy(freeMemPtr, 0x00, returndatasize()) revert(freeMemPtr, returndatasize()) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @notice Returns the current deposit balance of this account on the EntryPoint. /// @return result The current balance held at the EntryPoint. function getDeposit() external view virtual returns (uint256 result) { address entryPointAddress = _ENTRYPOINT; /// @solidity memory-safe-assembly assembly { mstore(0x20, address()) // Store the `account` argument. mstore(0x00, 0x70a08231) // `balanceOf(address)`. result := mul( // Returns 0 if the EntryPoint does not exist. mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), entryPointAddress, 0x1c, 0x24, 0x20, 0x20) ) ) } } /// @notice Retrieves the address of the EntryPoint contract, currently using version 0.7. /// @dev This function returns the address of the canonical ERC4337 EntryPoint contract. /// It can be overridden to return a different EntryPoint address if needed. /// @return The address of the EntryPoint contract. function entryPoint() external view returns (address) { return _ENTRYPOINT; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { SentinelListLib } from "sentinellist/SentinelList.sol"; import { Storage } from "./Storage.sol"; import { IModule, IValidator, IExecutor, IHook, IPreValidationHookERC1271, IPreValidationHookERC4337, IFallback } from "erc7579/interfaces/IERC7579Module.sol"; import { CallType, CALLTYPE_SINGLE, CALLTYPE_STATIC } from "../../lib/erc-7579/ModeLib.sol"; import { ExecLib } from "../../lib/erc-7579/ExecLib.sol"; import { LocalCallDataParserLib } from "../../lib/nexus/local/LocalCallDataParserLib.sol"; import { IModuleManager } from "../../interfaces/nexus/base/IModuleManager.sol"; // solhint-disable no-unused-import import { MODULE_TYPE_VALIDATOR, MODULE_TYPE_EXECUTOR, MODULE_TYPE_FALLBACK, MODULE_TYPE_HOOK, MODULE_TYPE_PREVALIDATION_HOOK_ERC1271, MODULE_TYPE_PREVALIDATION_HOOK_ERC4337, MODULE_TYPE_MULTI, MODULE_ENABLE_MODE_TYPE_HASH, EMERGENCY_UNINSTALL_TYPE_HASH, ERC1271_SUCCESS } from "../../types/Constants.sol"; // solhint-enable no-unused-import import { EIP712 } from "solady/utils/EIP712.sol"; import { ExcessivelySafeCall } from "excessively-safe-call/ExcessivelySafeCall.sol"; import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; import { EmergencyUninstall } from "../../types/DataTypes.sol"; /// @title Nexus - ModuleManager /// @notice Manages Validator, Executor, Hook, and Fallback modules within the Nexus suite, supporting /// @dev Implements SentinelList for managing modules via a linked list structure, adhering to ERC-7579. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady abstract contract ModuleManager is Storage, EIP712, IModuleManager { using SentinelListLib for SentinelListLib.SentinelList; using LocalCallDataParserLib for bytes; using ExecLib for address; using ExcessivelySafeCall for address; /// @dev The default validator address. /// @notice To explicitly initialize the default validator, Nexus.execute(_DEFAULT_VALIDATOR.onInstall(...)) should /// be /// called. address internal immutable _DEFAULT_VALIDATOR; /// @notice Ensures the message sender is a registered executor module. modifier onlyExecutorModule() virtual { require(_getAccountStorage().executors.contains(msg.sender), InvalidModule(msg.sender)); _; } /// @notice Does pre-checks and post-checks using an installed hook on the account. /// @dev sender, msg.data and msg.value is passed to the hook to implement custom flows. modifier withHook() { address hook = _getHook(); if (hook == address(0)) { _; } else { bytes memory hookData = IHook(hook).preCheck(msg.sender, msg.value, msg.data); _; IHook(hook).postCheck(hookData); } } /// @dev initData should block the implementation from being used as a Smart Account constructor(address defaultValidator_, bytes memory initData_) { if (!IValidator(defaultValidator_).isModuleType(MODULE_TYPE_VALIDATOR)) { revert MismatchModuleTypeId(); } IValidator(defaultValidator_).onInstall(initData_); _DEFAULT_VALIDATOR = defaultValidator_; } // receive function receive() external payable { } /// @dev Fallback function to manage incoming calls using designated handlers based on the call type. /// Hooked manually in the _fallback function fallback() external payable { _fallback(msg.data); } /// @dev Retrieves the default validator address. /// @return The address of the default validator. function getDefaultValidator() external view returns (address) { return _DEFAULT_VALIDATOR; } /// @dev Retrieves a paginated list of validator addresses from the linked list. /// This utility function is not defined by the ERC-7579 standard and is implemented to facilitate /// easier management and retrieval of large sets of validator modules. /// @param cursor The address to start pagination from, or zero to start from the first entry. /// @param size The number of validator addresses to return. /// @return array An array of validator addresses. /// @return next The address to use as a cursor for the next page of results. function getValidatorsPaginated( address cursor, uint256 size ) external view returns (address[] memory array, address next) { (array, next) = _paginate(_getAccountStorage().validators, cursor, size); } /// @dev Retrieves a paginated list of executor addresses from the linked list. /// This utility function is not defined by the ERC-7579 standard and is implemented to facilitate /// easier management and retrieval of large sets of executor modules. /// @param cursor The address to start pagination from, or zero to start from the first entry. /// @param size The number of executor addresses to return. /// @return array An array of executor addresses. /// @return next The address to use as a cursor for the next page of results. function getExecutorsPaginated( address cursor, uint256 size ) external view returns (address[] memory array, address next) { (array, next) = _paginate(_getAccountStorage().executors, cursor, size); } /// @notice Retrieves the currently active hook address. /// @return hook The address of the active hook module. function getActiveHook() external view returns (address hook) { return _getHook(); } /// @notice Fetches the fallback handler for a specific selector. /// @param selector The function selector to query. /// @return calltype The type of call that the handler manages. /// @return handler The address of the fallback handler. function getFallbackHandlerBySelector(bytes4 selector) external view returns (CallType, address) { FallbackHandler memory handler = _getAccountStorage().fallbacks[selector]; return (handler.calltype, handler.handler); } /// @dev Initializes the module manager by setting up default states for validators and executors. function _initSentinelLists() internal virtual { // account module storage AccountStorage storage ams = _getAccountStorage(); ams.executors.init(); ams.validators.init(); } /// @dev Implements Module Enable Mode flow. /// @param packedData Data source to parse data required to perform Module Enable mode from. /// @return userOpSignature the clean signature which can be further used for userOp validation function _enableMode( bytes32 userOpHash, bytes calldata packedData ) internal returns (bytes calldata userOpSignature) { address module; uint256 moduleType; bytes calldata moduleInitData; bytes calldata enableModeSignature; (module, moduleType, moduleInitData, enableModeSignature, userOpSignature) = packedData.parseEnableModeData(); address enableModeSigValidator = _handleValidator(address(bytes20(enableModeSignature[0:20]))); enableModeSignature = enableModeSignature[20:]; bytes32 structHash = _getEnableModeDataHash(module, moduleType, userOpHash, moduleInitData); bool isValid = _checkEnableModeSignature(structHash, enableModeSignature, enableModeSigValidator); assembly { if iszero(isValid) { // revert EnableModeSigError() mstore(0x00, 0x46fdc333) revert(0x1c, 0x04) } } this.installModule(moduleType, module, moduleInitData); } /// @notice Installs a new module to the smart account. /// @param moduleTypeId The type identifier of the module being installed, which determines its role: /// - 0 for MultiType /// - 1 for Validator /// - 2 for Executor /// - 3 for Fallback /// - 4 for Hook /// - 8 for PreValidationHookERC1271 /// - 9 for PreValidationHookERC4337 /// @param module The address of the module to install. /// @param initData Initialization data for the module. /// @dev This function goes through hook checks via withHook modifier. /// @dev No need to check that the module is already installed, as this check is done /// when trying to sstore the module in an appropriate SentinelList function _installModule(uint256 moduleTypeId, address module, bytes calldata initData) internal { if (!_areSentinelListsInitialized()) { _initSentinelLists(); } if (module == address(0)) revert ModuleAddressCanNotBeZero(); if (moduleTypeId == MODULE_TYPE_VALIDATOR) { _installValidator(module, initData); } else if (moduleTypeId == MODULE_TYPE_EXECUTOR) { _installExecutor(module, initData); } else if (moduleTypeId == MODULE_TYPE_FALLBACK) { _installFallbackHandler(module, initData); } else if (moduleTypeId == MODULE_TYPE_HOOK) { _installHook(module, initData); } else if (_isPrevalidationHookType(moduleTypeId)) { _installPreValidationHook(moduleTypeId, module, initData); } else if (moduleTypeId == MODULE_TYPE_MULTI) { _multiTypeInstall(module, initData); } else { revert InvalidModuleTypeId(moduleTypeId); } } /// @dev Installs a new validator module after checking if it matches the required module type. /// @param validator The address of the validator module to be installed. /// @param data Initialization data to configure the validator upon installation. function _installValidator(address validator, bytes calldata data) internal virtual withHook { if (!IValidator(validator).isModuleType(MODULE_TYPE_VALIDATOR)) revert MismatchModuleTypeId(); if (validator == _DEFAULT_VALIDATOR) { revert DefaultValidatorAlreadyInstalled(); } _getAccountStorage().validators.push(validator); IValidator(validator).onInstall(data); } /// @dev Uninstalls a validator module. /// @param validator The address of the validator to be uninstalled. /// @param data De-initialization data to configure the validator upon uninstallation. function _uninstallValidator(address validator, bytes calldata data) internal virtual { SentinelListLib.SentinelList storage validators = _getAccountStorage().validators; (address prev, bytes memory disableModuleData) = abi.decode(data, (address, bytes)); // Perform the removal first validators.pop(prev, validator); validator.excessivelySafeCall( gasleft(), 0, 0, abi.encodeWithSelector(IModule.onUninstall.selector, disableModuleData) ); } /// @dev Installs a new executor module after checking if it matches the required module type. /// @param executor The address of the executor module to be installed. /// @param data Initialization data to configure the executor upon installation. function _installExecutor(address executor, bytes calldata data) internal virtual withHook { if (!IExecutor(executor).isModuleType(MODULE_TYPE_EXECUTOR)) revert MismatchModuleTypeId(); _getAccountStorage().executors.push(executor); IExecutor(executor).onInstall(data); } /// @dev Uninstalls an executor module by removing it from the executors list. /// @param executor The address of the executor to be uninstalled. /// @param data De-initialization data to configure the executor upon uninstallation. function _uninstallExecutor(address executor, bytes calldata data) internal virtual { (address prev, bytes memory disableModuleData) = abi.decode(data, (address, bytes)); _getAccountStorage().executors.pop(prev, executor); executor.excessivelySafeCall( gasleft(), 0, 0, abi.encodeWithSelector(IModule.onUninstall.selector, disableModuleData) ); } /// @dev Installs a hook module, ensuring no other hooks are installed before proceeding. /// @param hook The address of the hook to be installed. /// @param data Initialization data to configure the hook upon installation. function _installHook(address hook, bytes calldata data) internal virtual withHook { if (!IHook(hook).isModuleType(MODULE_TYPE_HOOK)) revert MismatchModuleTypeId(); address currentHook = _getHook(); require(currentHook == address(0), HookAlreadyInstalled(currentHook)); _setHook(hook); IHook(hook).onInstall(data); } /// @dev Uninstalls a hook module, ensuring the current hook matches the one intended for uninstallation. /// @param hook The address of the hook to be uninstalled. /// @param hookType The type of the hook to be uninstalled. /// @param data De-initialization data to configure the hook upon uninstallation. function _uninstallHook(address hook, uint256 hookType, bytes calldata data) internal virtual { if (hookType == MODULE_TYPE_HOOK) { _setHook(address(0)); } else if (_isPrevalidationHookType(hookType)) { _uninstallPreValidationHook(hookType); } hook.excessivelySafeCall(gasleft(), 0, 0, abi.encodeWithSelector(IModule.onUninstall.selector, data)); } /// @dev Sets the current hook in the storage to the specified address. /// @param hook The new hook address. function _setHook(address hook) internal virtual { _getAccountStorage().hook = IHook(hook); } /// @dev Installs a fallback handler for a given selector with initialization data. /// @param handler The address of the fallback handler to install. /// @param params The initialization parameters including the selector and call type. function _installFallbackHandler(address handler, bytes calldata params) internal virtual withHook { if (!IFallback(handler).isModuleType(MODULE_TYPE_FALLBACK)) revert MismatchModuleTypeId(); // Extract the function selector from the provided parameters. bytes4 selector = bytes4(params[0:4]); // Extract the call type from the provided parameters. CallType calltype = CallType.wrap(bytes1(params[4])); require(calltype == CALLTYPE_SINGLE || calltype == CALLTYPE_STATIC, FallbackCallTypeInvalid()); // Extract the initialization data from the provided parameters. bytes memory initData = params[5:]; // Revert if the selector is either `onInstall(bytes)` (0x6d61fe70) or `onUninstall(bytes)` (0x8a91b0e3) or // explicit bytes(0). // These selectors are explicitly forbidden to prevent security vulnerabilities. // Allowing these selectors would enable unauthorized users to uninstall and reinstall critical modules. // If a validator module is uninstalled and reinstalled without proper authorization, it can compromise // the account's security and integrity. By restricting these selectors, we ensure that the fallback handler // cannot be manipulated to disrupt the expected behavior and security of the account. require( !(selector == bytes4(0x6d61fe70) || selector == bytes4(0x8a91b0e3) || selector == bytes4(0)), FallbackSelectorForbidden() ); // Revert if a fallback handler is already installed for the given selector. // This check ensures that we do not overwrite an existing fallback handler, which could lead to unexpected // behavior. require(!_isFallbackHandlerInstalled(selector), FallbackAlreadyInstalledForSelector(selector)); // Store the fallback handler and its call type in the account storage. // This maps the function selector to the specified fallback handler and call type. _getAccountStorage().fallbacks[selector] = FallbackHandler(handler, calltype); // Invoke the `onInstall` function of the fallback handler with the provided initialization data. // This step allows the fallback handler to perform any necessary setup or initialization. IFallback(handler).onInstall(initData); } /// @dev Uninstalls a fallback handler for a given selector. /// @param fallbackHandler The address of the fallback handler to uninstall. /// @param data The de-initialization data containing the selector. function _uninstallFallbackHandler(address fallbackHandler, bytes calldata data) internal virtual { _getAccountStorage().fallbacks[bytes4(data[0:4])] = FallbackHandler(address(0), CallType.wrap(0x00)); fallbackHandler.excessivelySafeCall( gasleft(), 0, 0, abi.encodeWithSelector(IModule.onUninstall.selector, data[4:]) ); } /// @dev Installs a pre-validation hook module, ensuring no other pre-validation hooks are installed before /// proceeding. /// @param preValidationHookType The type of the pre-validation hook. /// @param preValidationHook The address of the pre-validation hook to be installed. /// @param data Initialization data to configure the hook upon installation. function _installPreValidationHook( uint256 preValidationHookType, address preValidationHook, bytes calldata data ) internal virtual withHook { if (!IModule(preValidationHook).isModuleType(preValidationHookType)) revert MismatchModuleTypeId(); address currentPreValidationHook = _getPreValidationHook(preValidationHookType); require(currentPreValidationHook == address(0), PrevalidationHookAlreadyInstalled(currentPreValidationHook)); _setPreValidationHook(preValidationHookType, preValidationHook); IModule(preValidationHook).onInstall(data); } /// @dev Uninstalls a pre-validation hook module /// @param hookType The type of the pre-validation hook. function _uninstallPreValidationHook(uint256 hookType) internal virtual { _setPreValidationHook(hookType, address(0)); } /// @dev Sets the current pre-validation hook in the storage to the specified address, based on the hook type. /// @param hookType The type of the pre-validation hook. /// @param hook The new hook address. function _setPreValidationHook(uint256 hookType, address hook) internal virtual { if (hookType == MODULE_TYPE_PREVALIDATION_HOOK_ERC1271) { _getAccountStorage().preValidationHookERC1271 = IPreValidationHookERC1271(hook); } else if (hookType == MODULE_TYPE_PREVALIDATION_HOOK_ERC4337) { _getAccountStorage().preValidationHookERC4337 = IPreValidationHookERC4337(hook); } } /// @notice Installs a module with multiple types in a single operation. /// @dev This function handles installing a multi-type module by iterating through each type and initializing it. /// The initData should include an ABI-encoded tuple of (uint[] types, bytes[] initDatas). /// @param module The address of the multi-type module. /// @param initData Initialization data for each type within the module. function _multiTypeInstall(address module, bytes calldata initData) internal virtual { (uint256[] calldata types, bytes[] calldata initDatas) = initData.parseMultiTypeInitData(); uint256 length = types.length; if (initDatas.length != length) revert InvalidInput(); // iterate over all module types and install the module as a type accordingly for (uint256 i; i < length; i++) { uint256 theType = types[i]; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INSTALL VALIDATORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ if (theType == MODULE_TYPE_VALIDATOR) { _installValidator(module, initDatas[i]); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INSTALL EXECUTORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ else if (theType == MODULE_TYPE_EXECUTOR) { _installExecutor(module, initDatas[i]); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INSTALL FALLBACK */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ else if (theType == MODULE_TYPE_FALLBACK) { _installFallbackHandler(module, initDatas[i]); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INSTALL HOOK (global only, not sig-specific) */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ else if (theType == MODULE_TYPE_HOOK) { _installHook(module, initDatas[i]); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INSTALL PRE-VALIDATION HOOK */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ else if (_isPrevalidationHookType(theType)) { _installPreValidationHook(theType, module, initDatas[i]); } } } /// @notice Checks if an emergency uninstall signature is valid. /// @param data The emergency uninstall data. /// @param signature The signature to validate. function _checkEmergencyUninstallSignature(EmergencyUninstall calldata data, bytes calldata signature) internal { address validator = _handleValidator(address(bytes20(signature[0:20]))); // Hash the data bytes32 hash = _hashTypedData( keccak256( abi.encode( EMERGENCY_UNINSTALL_TYPE_HASH, data.hook, data.hookType, keccak256(data.deInitData), data.nonce ) ) ); // Check if nonce is valid require(!_getAccountStorage().nonces[data.nonce], InvalidNonce()); // Mark nonce as used _getAccountStorage().nonces[data.nonce] = true; // Check if the signature is valid require( (IValidator(validator).isValidSignatureWithSender(address(this), hash, signature[20:]) == ERC1271_SUCCESS), EmergencyUninstallSigError() ); } /// @dev Calls the pre-validation hook for ERC-4337. /// @param hash The hash of the user operation. /// @param userOp The user operation data. /// @param missingAccountFunds The amount of missing account funds. /// @return postHash The updated hash after the pre-validation hook. /// @return postSig The updated signature after the pre-validation hook. function _withPreValidationHook( bytes32 hash, PackedUserOperation memory userOp, uint256 missingAccountFunds ) internal virtual returns (bytes32 postHash, bytes memory postSig) { // Get the pre-validation hook for ERC-4337 address preValidationHook = _getPreValidationHook(MODULE_TYPE_PREVALIDATION_HOOK_ERC4337); // If no pre-validation hook is installed, return the original hash and signature if (preValidationHook == address(0)) { return (hash, userOp.signature); } // Otherwise, call the pre-validation hook and return the updated hash and signature else { return IPreValidationHookERC4337(preValidationHook).preValidationHookERC4337(userOp, missingAccountFunds, hash); } } /// @dev Retrieves the pre-validation hook from the storage based on the hook type. /// @param preValidationHookType The type of the pre-validation hook. /// @return preValidationHook The address of the pre-validation hook. function _getPreValidationHook(uint256 preValidationHookType) internal view returns (address preValidationHook) { preValidationHook = preValidationHookType == MODULE_TYPE_PREVALIDATION_HOOK_ERC1271 ? address(_getAccountStorage().preValidationHookERC1271) : address(_getAccountStorage().preValidationHookERC4337); } /// @dev Calls the pre-validation hook for ERC-1271. /// @param hash The hash of the user operation. /// @param signature The signature to validate. /// @return postHash The updated hash after the pre-validation hook. /// @return postSig The updated signature after the pre-validation hook. function _withPreValidationHook( bytes32 hash, bytes calldata signature ) internal view virtual returns (bytes32 postHash, bytes memory postSig) { // Get the pre-validation hook for ERC-1271 address preValidationHook = _getPreValidationHook(MODULE_TYPE_PREVALIDATION_HOOK_ERC1271); // If no pre-validation hook is installed, return the original hash and signature if (preValidationHook == address(0)) { return (hash, signature); } // Otherwise, call the pre-validation hook and return the updated hash and signature else { return IPreValidationHookERC1271(preValidationHook).preValidationHookERC1271(msg.sender, hash, signature); } } /// @notice Checks if an enable mode signature is valid. /// @param structHash data hash. /// @param sig Signature. /// @param validator Validator address. function _checkEnableModeSignature( bytes32 structHash, bytes calldata sig, address validator ) internal view returns (bool) { bytes32 eip712Digest = _hashTypedData(structHash); // Use standard IERC-1271/ERC-7739 interface. // Even if the validator doesn't support 7739 under the hood, it is still secure, // as eip712digest is already built based on 712Domain of this Smart Account // This interface should always be exposed by validators as per ERC-7579 try IValidator(validator).isValidSignatureWithSender(address(this), eip712Digest, sig) returns (bytes4 res) { return res == ERC1271_SUCCESS; } catch { return false; } } /// @notice Checks if a module is installed on the smart account. /// @param moduleTypeId The module type ID. /// @param module The module address. /// @param additionalContext Additional context for checking installation. /// @return True if the module is installed, false otherwise. function _isModuleInstalled( uint256 moduleTypeId, address module, bytes calldata additionalContext ) internal view returns (bool) { additionalContext; if (moduleTypeId == MODULE_TYPE_VALIDATOR) { return _isValidatorInstalled(module); } else if (moduleTypeId == MODULE_TYPE_EXECUTOR) { return _isExecutorInstalled(module); } else if (moduleTypeId == MODULE_TYPE_FALLBACK) { bytes4 selector; if (additionalContext.length >= 4) { selector = bytes4(additionalContext[0:4]); } else { selector = bytes4(0x00000000); } return _isFallbackHandlerInstalled(selector, module); } else if (moduleTypeId == MODULE_TYPE_HOOK) { return _isHookInstalled(module); } else if (_isPrevalidationHookType(moduleTypeId)) { return _getPreValidationHook(moduleTypeId) == module; } else { return false; } } /// @dev Does bytecode optimized check of the module type id being a pre-validation hook type. /// @param moduleTypeId The module type id to check. /// return True if the module type id is a pre-validation hook type, otherwise false. /// If theType == 8: 8 - 8 = 0, and 0 < 2 ✓ /// If theType == 9: 9 - 8 = 1, and 1 < 2 ✓ /// If theType < 8 (e.g., 7): 7 - 8 = type(uint256).max (wraps), and type(uint256).max < 2 is false ✓ /// If theType >= 10: result is >= 2, and fails the check ✓ function _isPrevalidationHookType(uint256 moduleTypeId) internal pure returns (bool res) { assembly { res := lt(sub(moduleTypeId, 8), 2) } } /// @dev Checks if the validator list is already initialized. /// In theory it doesn't 100% mean there is a validator or executor installed. /// Use below functions to check for validators and executors. function _areSentinelListsInitialized() internal view virtual returns (bool) { // account module storage AccountStorage storage ams = _getAccountStorage(); return ams.validators.alreadyInitialized() && ams.executors.alreadyInitialized(); } /// @dev Checks if a fallback handler is set for a given selector. /// @param selector The function selector to check. /// @return True if a fallback handler is set, otherwise false. function _isFallbackHandlerInstalled(bytes4 selector) internal view virtual returns (bool) { FallbackHandler storage handler = _getAccountStorage().fallbacks[selector]; return handler.handler != address(0); } /// @dev Checks if the expected fallback handler is installed for a given selector. /// @param selector The function selector to check. /// @param expectedHandler The address of the handler expected to be installed. /// @return True if the installed handler matches the expected handler, otherwise false. function _isFallbackHandlerInstalled(bytes4 selector, address expectedHandler) internal view returns (bool) { FallbackHandler storage handler = _getAccountStorage().fallbacks[selector]; return handler.handler == expectedHandler; } /// @dev Checks if a validator is currently installed. /// @param validator The address of the validator to check. /// @return True if the validator is installed, otherwise false. function _isValidatorInstalled(address validator) internal view virtual returns (bool) { return _getAccountStorage().validators.contains(validator); } /// @dev Checks if an executor is currently installed. /// @param executor The address of the executor to check. /// @return True if the executor is installed, otherwise false. function _isExecutorInstalled(address executor) internal view virtual returns (bool) { return _getAccountStorage().executors.contains(executor); } /// @dev Checks if a hook is currently installed. /// @param hook The address of the hook to check. /// @return True if the hook is installed, otherwise false. function _isHookInstalled(address hook) internal view returns (bool) { return _getHook() == hook; } /// @dev Retrieves the current hook from the storage. /// @return hook The address of the current hook. function _getHook() internal view returns (address hook) { hook = address(_getAccountStorage().hook); } /// @dev Checks if the account is an ERC7702 account function _amIERC7702() internal view returns (bool res) { assembly { // use extcodesize as the first cheapest check if eq(extcodesize(address()), 23) { // use extcodecopy to copy first 3 bytes of this contract and compare with 0xef0100 extcodecopy(address(), 0, 0, 3) res := eq(0xef0100, shr(232, mload(0x00))) } // if it is not 23, we do not even check the first 3 bytes } } /// @dev Returns the validator address to use function _handleValidator(address _validator) internal view returns (address validator) { if (_validator == address(0)) { validator = _DEFAULT_VALIDATOR; } else { if (!_isValidatorInstalled(_validator)) { assembly { // revert ValidatorNotInstalled(address) mstore(0x00, 0x6859e01e) mstore(0x20, _validator) revert(0x1c, 0x24) } } validator = _validator; } } /// @notice Builds the enable mode data hash as per eip712 /// @param module Module being enabled /// @param moduleType Type of the module as per EIP-7579 /// @param userOpHash Hash of the User Operation /// @param initData Module init data. /// @return structHash data hash function _getEnableModeDataHash( address module, uint256 moduleType, bytes32 userOpHash, bytes calldata initData ) internal pure returns (bytes32) { bytes32 structHash; assembly { let ptr := mload(0x40) mstore(ptr, MODULE_ENABLE_MODE_TYPE_HASH) mstore(add(ptr, 0x20), module) mstore(add(ptr, 0x40), moduleType) mstore(add(ptr, 0x60), userOpHash) // Hash initData and store at ptr + 0x80 // calldatacopy to copy initData to memory, then hash it let initDataPtr := add(ptr, 0xa0) calldatacopy(initDataPtr, initData.offset, initData.length) let initDataHash := keccak256(initDataPtr, initData.length) mstore(add(ptr, 0x80), initDataHash) // Hash the entire struct structHash := keccak256(ptr, 0xa0) } return structHash; } function _fallback(bytes calldata callData) private { bool success; bytes memory result; FallbackHandler storage $fallbackHandler = _getAccountStorage().fallbacks[msg.sig]; address handler = $fallbackHandler.handler; CallType calltype = $fallbackHandler.calltype; if (handler != address(0)) { // hook manually address hook = _getHook(); bytes memory hookData; if (hook != address(0)) { hookData = IHook(hook).preCheck(msg.sender, msg.value, msg.data); } //if there's a fallback handler, call it if (calltype == CALLTYPE_STATIC) { (success, result) = handler.staticcall(ExecLib.get2771CallData(callData)); } else if (calltype == CALLTYPE_SINGLE) { (success, result) = handler.call{ value: msg.value }(ExecLib.get2771CallData(callData)); } else { revert UnsupportedCallType(calltype); } // Use revert message from fallback handler if the call was not successful assembly { if iszero(success) { revert(add(result, 0x20), mload(result)) } } // hook post check if (hook != address(0)) { IHook(hook).postCheck(hookData); } // return the result assembly { return(add(result, 0x20), mload(result)) } } // If there's no handler, the call can be one of onERCXXXReceived() // No need to hook this as no execution is done here bytes32 s; /// @solidity memory-safe-assembly assembly { s := shr(224, calldataload(0)) // 0x150b7a02: `onERC721Received(address,address,uint256,bytes)`. // 0xf23a6e61: `onERC1155Received(address,address,uint256,uint256,bytes)`. // 0xbc197c81: `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`. if or(eq(s, 0x150b7a02), or(eq(s, 0xf23a6e61), eq(s, 0xbc197c81))) { mstore(0x20, s) // Store `msg.sig`. return(0x3c, 0x20) // Return `msg.sig`. } mstore(0x00, 0x08c63e27) mstore(0x20, shl(224, s)) // Left-align bytes4 for ABI encoding revert(0x1c, 0x24) // revert MissingFallbackHandler(msg.sig) } } /// @dev Helper function to paginate entries in a SentinelList. /// @param list The SentinelList to paginate. /// @param cursor The cursor to start paginating from. /// @param size The number of entries to return. /// @return array The array of addresses in the list. /// @return nextCursor The cursor for the next page of entries. function _paginate( SentinelListLib.SentinelList storage list, address cursor, uint256 size ) private view returns (address[] memory array, address nextCursor) { (array, nextCursor) = list.getEntriesPaginated(cursor, size); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { Execution } from "erc7579/interfaces/IERC7579Account.sol"; import { IExecutionHelperEventsAndErrors } from "../../interfaces/nexus/base/IExecutionHelper.sol"; import { ExecType, EXECTYPE_DEFAULT, EXECTYPE_TRY } from "../../lib/erc-7579/ModeLib.sol"; import { ExecLib } from "../../lib/erc-7579/ExecLib.sol"; /// @title Nexus - ExecutionHelper /// @notice Implements execution management within the Nexus suite, facilitating transaction execution strategies and /// error handling. /// @dev Provides mechanisms for direct and batched transactions with both committed and tentative execution strategies /// as per ERC-4337 and ERC-7579 standards. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady contract ExecutionHelper is IExecutionHelperEventsAndErrors { using ExecLib for bytes; /// @notice Executes a call to a target address with specified value and data. /// @notice calls to an EOA should be counted as successful. /// @param target The address to execute the call on. /// @param value The amount of wei to send with the call. /// @param callData The calldata to send. /// @return result The bytes returned from the execution, which contains the returned data from the target address. function _execute( address target, uint256 value, bytes calldata callData ) internal virtual returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) calldatacopy(result, callData.offset, callData.length) if iszero(call(gas(), target, value, result, callData.length, codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } mstore(result, returndatasize()) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } /// @notice Executes a call to a target address with specified value and data. /// same as _execute, but callData can be in memory function _executeMemory( address target, uint256 value, bytes memory callData ) internal virtual returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) if iszero(call(gas(), target, value, add(callData, 0x20), mload(callData), codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } mstore(result, returndatasize()) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } /// @notice Executes a call to a target address with specified value and data. /// Same as _execute but without return data for gas optimization. function _executeNoReturndata(address target, uint256 value, bytes calldata callData) internal virtual { /// @solidity memory-safe-assembly assembly { let result := mload(0x40) calldatacopy(result, callData.offset, callData.length) if iszero(call(gas(), target, value, result, callData.length, codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } mstore(0x40, add(result, callData.length)) //allocate memory } } /// @notice Tries to execute a call and captures if it was successful or not. /// @dev Similar to _execute but returns a success boolean and catches reverts instead of propagating them. /// @notice calls to an EOA should be counted as successful. /// @param target The address to execute the call on. /// @param value The amount of wei to send with the call. /// @param callData The calldata to send. /// @return success True if the execution was successful, false otherwise. /// @return result The bytes returned from the execution, which contains the returned data from the target address. function _tryExecute( address target, uint256 value, bytes calldata callData ) internal virtual returns (bool success, bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) calldatacopy(result, callData.offset, callData.length) success := call(gas(), target, value, result, callData.length, codesize(), 0x00) mstore(result, returndatasize()) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } /// @notice Executes a batch of calls. /// @param executions An array of Execution structs each containing target, value, and calldata. /// @return result An array of bytes returned from each executed call, corresponding to the returndata from each /// target /// address. function _executeBatch(Execution[] calldata executions) internal returns (bytes[] memory result) { result = new bytes[](executions.length); Execution calldata exec; for (uint256 i; i < executions.length; i++) { exec = executions[i]; result[i] = _execute(exec.target, exec.value, exec.callData); } } /// @notice Executes a batch of calls without returning the result. /// @param executions An array of Execution structs each containing target, value, and calldata. function _executeBatchNoReturndata(Execution[] calldata executions) internal { Execution calldata exec; for (uint256 i; i < executions.length; i++) { exec = executions[i]; _executeNoReturndata(exec.target, exec.value, exec.callData); } } /// @notice Tries to execute a batch of calls and emits an event for each unsuccessful call. /// @param executions An array of Execution structs. /// @return result An array of bytes returned from each executed call, with unsuccessful calls marked by events. function _tryExecuteBatch(Execution[] calldata executions) internal returns (bytes[] memory result) { result = new bytes[](executions.length); Execution calldata exec; for (uint256 i; i < executions.length; i++) { exec = executions[i]; bool success; (success, result[i]) = _tryExecute(exec.target, exec.value, exec.callData); if (!success) emit TryExecuteUnsuccessful(exec.callData, result[i]); } } /// @dev Execute a delegatecall with `delegate` on this account. /// @return result The bytes returned from the delegatecall, which contains the returned data from the delegate /// contract. function _executeDelegatecall(address delegate, bytes calldata callData) internal returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) calldatacopy(result, callData.offset, callData.length) // Forwards the `data` to `delegate` via delegatecall. if iszero(delegatecall(gas(), delegate, result, callData.length, codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } mstore(result, returndatasize()) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } /// @dev Execute a delegatecall with `delegate` on this account. /// Same as _executeDelegatecall but without return data for gas optimization. function _executeDelegatecallNoReturndata(address delegate, bytes calldata callData) internal { /// @solidity memory-safe-assembly assembly { let result := mload(0x40) calldatacopy(result, callData.offset, callData.length) if iszero(delegatecall(gas(), delegate, result, callData.length, codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } mstore(0x40, add(result, callData.length)) //allocate memory } } /// @dev Execute a delegatecall with `delegate` on this account and catch reverts. /// @return success True if the delegatecall was successful, false otherwise. /// @return result The bytes returned from the delegatecall, which contains the returned data from the delegate /// contract. function _tryExecuteDelegatecall( address delegate, bytes calldata callData ) internal returns (bool success, bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) calldatacopy(result, callData.offset, callData.length) // Forwards the `data` to `delegate` via delegatecall. success := delegatecall(gas(), delegate, result, callData.length, codesize(), 0x00) mstore(result, returndatasize()) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } /// @dev Executes a single transaction based on the specified execution type. /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). function _handleSingleExecution(bytes calldata executionCalldata, ExecType execType) internal { (address target, uint256 value, bytes calldata callData) = executionCalldata.decodeSingle(); if (execType == EXECTYPE_DEFAULT) { _executeNoReturndata(target, value, callData); } else if (execType == EXECTYPE_TRY) { (bool success, bytes memory result) = _tryExecute(target, value, callData); if (!success) emit TryExecuteUnsuccessful(callData, result); } else { revert UnsupportedExecType(execType); } } /// @dev Executes a batch of transactions based on the specified execution type. /// @param executionCalldata The calldata for a batch of transactions. /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). function _handleBatchExecution(bytes calldata executionCalldata, ExecType execType) internal { Execution[] calldata executions = executionCalldata.decodeBatch(); if (execType == EXECTYPE_DEFAULT) _executeBatchNoReturndata(executions); else if (execType == EXECTYPE_TRY) _tryExecuteBatch(executions); else revert UnsupportedExecType(execType); } /// @dev Executes a single transaction based on the specified execution type. /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). function _handleDelegateCallExecution(bytes calldata executionCalldata, ExecType execType) internal { (address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall(); if (execType == EXECTYPE_DEFAULT) { _executeDelegatecallNoReturndata(delegate, callData); } else if (execType == EXECTYPE_TRY) { (bool success, bytes memory result) = _tryExecuteDelegatecall(delegate, callData); if (!success) emit TryDelegateCallUnsuccessful(callData, result); } else { revert UnsupportedExecType(execType); } } /// @dev Executes a single transaction based on the specified execution type. /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). /// @return returnData An array containing the execution result. In the case of a single transaction, the array /// contains one element. function _handleSingleExecutionAndReturnData( bytes calldata executionCalldata, ExecType execType ) internal returns (bytes[] memory returnData) { (address target, uint256 value, bytes calldata callData) = executionCalldata.decodeSingle(); returnData = new bytes[](1); bool success; // check if execType is revert(default) or try if (execType == EXECTYPE_DEFAULT) { returnData[0] = _execute(target, value, callData); } else if (execType == EXECTYPE_TRY) { (success, returnData[0]) = _tryExecute(target, value, callData); if (!success) emit TryExecuteUnsuccessful(callData, returnData[0]); } else { revert UnsupportedExecType(execType); } } /// @dev Executes a batch of transactions based on the specified execution type. /// @param executionCalldata The calldata for a batch of transactions. /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). /// @return returnData An array containing the execution results for each transaction in the batch. function _handleBatchExecutionAndReturnData( bytes calldata executionCalldata, ExecType execType ) internal returns (bytes[] memory returnData) { Execution[] calldata executions = executionCalldata.decodeBatch(); if (execType == EXECTYPE_DEFAULT) returnData = _executeBatch(executions); else if (execType == EXECTYPE_TRY) returnData = _tryExecuteBatch(executions); else revert UnsupportedExecType(execType); } /// @dev Executes a single transaction based on the specified execution type. /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). /// @return returnData An array containing the result of the delegatecall execution. function _handleDelegateCallExecutionAndReturnData( bytes calldata executionCalldata, ExecType execType ) internal returns (bytes[] memory returnData) { (address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall(); returnData = new bytes[](1); bool success; if (execType == EXECTYPE_DEFAULT) { returnData[0] = _executeDelegatecall(delegate, callData); } else if (execType == EXECTYPE_TRY) { (success, returnData[0]) = _tryExecuteDelegatecall(delegate, callData); if (!success) emit TryDelegateCallUnsuccessful(callData, returnData[0]); } else { revert UnsupportedExecType(execType); } } }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import { ComposableExecutionLib } from "./ComposableExecutionLib.sol";
import { InputParam, OutputParam, ComposableExecution } from "../types/ComposabilityDataTypes.sol";
import { IComposableExecution } from "../interfaces/IComposableExecution.sol";
import { Execution } from "erc7579/interfaces/IERC7579Account.sol";
abstract contract ComposableExecutionBase is IComposableExecution {
using ComposableExecutionLib for InputParam[];
using ComposableExecutionLib for OutputParam[];
/// @dev Override it in the account and introduce additional access control or other checks
function executeComposable(ComposableExecution[] calldata cExecutions) external payable virtual;
/// @dev internal function to execute the composable execution flow
/// First, processes the input parameters and returns the composed calldata
/// Then, executes the action
/// Then, processes the output parameters
function _executeComposable(ComposableExecution[] calldata cExecutions) internal {
uint256 length = cExecutions.length;
for (uint256 i; i < length; i++) {
ComposableExecution calldata cExecution = cExecutions[i];
Execution memory execution = cExecution.inputParams.processInputs(cExecution.functionSig);
bytes memory returnData;
if (execution.target != address(0)) {
returnData = _executeAction(execution.target, execution.value, execution.callData);
} else {
returnData = new bytes(0);
}
// TODO: add early sanity check that output params length is > 0
// so if it is 0, we can not even call processOutputs
cExecution.outputParams.processOutputs(returnData, address(this));
}
}
/// @dev Override this in the account
/// using account's native execution approach
/// we do not use Execution struct as an argument to be as less opinionated as possible
/// instead we just use standard types
function _executeAction(
address to,
uint256 value,
bytes memory data
)
internal
virtual
returns (bytes memory returnData);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
// keccak256(abi.encode(uint256(keccak256("initializable.transient.Nexus")) - 1)) & ~bytes32(uint256(0xff));
bytes32 constant INIT_SLOT = 0x90b772c2cb8a51aa7a8a65fc23543c6d022d5b3f8e2b92eed79fba7eef829300;
/// @title Initializable
/// @dev This library provides a way to set a transient flag on a contract to ensure that it is only initialized during
/// the
/// constructor execution. This is useful to prevent a contract from being initialized multiple times.
library Initializable {
/// @dev Thrown when an attempt to initialize an already initialized contract is made
error NotInitializable();
/// @dev Sets the initializable flag in the transient storage slot to true
function setInitializable() internal {
bytes32 slot = INIT_SLOT;
assembly {
tstore(slot, 0x01)
}
}
/// @dev Checks if the initializable flag is set in the transient storage slot, reverts with NotInitializable if not
function requireInitializable() internal view {
bytes32 slot = INIT_SLOT;
// Load the current value from the slot, revert if 0
assembly {
let isInitializable := tload(slot)
if iszero(isInitializable) {
mstore(0x0, 0xaed59595) // NotInitializable()
revert(0x1c, 0x04)
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {CallContextChecker} from "./CallContextChecker.sol";
/// @notice UUPS proxy mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol)
/// @author Modified from OpenZeppelin
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol)
///
/// @dev Note:
/// - This implementation is intended to be used with ERC1967 proxies.
/// See: `LibClone.deployERC1967` and related functions.
/// - This implementation is NOT compatible with legacy OpenZeppelin proxies
/// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`.
abstract contract UUPSUpgradeable is CallContextChecker {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The upgrade failed.
error UpgradeFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when the proxy's implementation is upgraded.
event Upgraded(address indexed implementation);
/// @dev `keccak256(bytes("Upgraded(address)"))`.
uint256 private constant _UPGRADED_EVENT_SIGNATURE =
0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ERC-1967 storage slot for the implementation in the proxy.
/// `uint256(keccak256("eip1967.proxy.implementation")) - 1`.
bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UUPS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to check if `msg.sender` is authorized
/// to upgrade the proxy to `newImplementation`, reverting if not.
/// ```
/// function _authorizeUpgrade(address) internal override onlyOwner {}
/// ```
function _authorizeUpgrade(address newImplementation) internal virtual;
/// @dev Returns the storage slot used by the implementation,
/// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822).
///
/// Note: The `notDelegated` modifier prevents accidental upgrades to
/// an implementation that is a proxy contract.
function proxiableUUID() public view virtual notDelegated returns (bytes32) {
// This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967.
return _ERC1967_IMPLEMENTATION_SLOT;
}
/// @dev Upgrades the proxy's implementation to `newImplementation`.
/// Emits a {Upgraded} event.
///
/// Note: Passing in empty `data` skips the delegatecall to `newImplementation`.
function upgradeToAndCall(address newImplementation, bytes calldata data)
public
payable
virtual
onlyProxy
{
_authorizeUpgrade(newImplementation);
/// @solidity memory-safe-assembly
assembly {
newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits.
mstore(0x00, returndatasize())
mstore(0x01, 0x52d1902d) // `proxiableUUID()`.
let s := _ERC1967_IMPLEMENTATION_SLOT
// Check if `newImplementation` implements `proxiableUUID` correctly.
if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) {
mstore(0x01, 0x55299b49) // `UpgradeFailed()`.
revert(0x1d, 0x04)
}
// Emit the {Upgraded} event.
log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation)
sstore(s, newImplementation) // Updates the implementation.
// Perform a delegatecall to `newImplementation` if `data` is non-empty.
if data.length {
// Forwards the `data` to `newImplementation` via delegatecall.
let m := mload(0x40)
calldatacopy(m, data.offset, data.length)
if iszero(
delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00)
) {
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IAccountConfig } from "./base/IAccountConfig.sol"; import { IExecutionHelper } from "./base/IExecutionHelper.sol"; import { INexusEventsAndErrors } from "./INexusEventsAndErrors.sol"; import { IERC1271 } from "../standard/IERC1271.sol"; import { IAccount } from "account-abstraction/interfaces/IAccount.sol"; /// @title Nexus - INexus Interface /// @notice Integrates ERC-4337 and ERC-7579 standards to manage smart accounts within the Nexus suite. /// @dev Consolidates ERC-4337 user operations and ERC-7579 configurations into a unified interface for smart account /// management. /// It extends both IERC4337Account and IERC7579Account, enhancing modular capabilities and supporting advanced contract /// architectures. /// Includes error definitions for robust handling of common issues such as unsupported module types and execution /// failures. /// The initialize function sets up the account with validators and configurations, ensuring readiness for use. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface INexus is IAccountConfig, IExecutionHelper, IERC1271, IAccount, INexusEventsAndErrors { /// @notice Initializes the smart account with a validator and custom data. /// @dev This method sets up the account for operation, linking it with a validator and initializing it with /// specific /// data. /// Can be called directly or via a factory. /// @param initData Encoded data used for the account's configuration during initialization. function initializeAccount(bytes calldata initData) external payable; }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import { Execution } from "erc7579/interfaces/IERC7579Account.sol";
/// @title ExecutionLib
/// @author zeroknots.eth | rhinestone.wtf
/// Helper Library for decoding Execution calldata
/// malloc for memory allocation is bad for gas. use this assembly instead
library ExecLib {
function get2771CallData(bytes calldata cd) internal view returns (bytes memory callData) {
/// @solidity memory-safe-assembly
(cd);
assembly {
// as per solidity docs
function allocate(length) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, length))
}
callData := allocate(add(calldatasize(), 0x20)) //allocate extra 0x20 to store length
mstore(callData, add(calldatasize(), 0x14)) //store length, extra 0x14 is for msg.sender address
calldatacopy(add(callData, 0x20), 0, calldatasize())
// The msg.sender address is shifted to the left by 12 bytes to remove the padding
// Then the address without padding is stored right after the calldata
let senderPtr := allocate(0x14)
mstore(senderPtr, shl(96, caller()))
}
}
/**
* @notice Decode a batch of `Execution` executionBatch from a `bytes` calldata.
* @dev code is copied from solady's LibERC7579.sol
* https://github.com/Vectorized/solady/blob/740812cedc9a1fc11e17cb3d4569744367dedf19/src/accounts/LibERC7579.sol#L146
* Credits to Vectorized and the Solady Team
*/
function decodeBatch(bytes calldata executionCalldata) internal pure returns (Execution[] calldata executionBatch) {
/// @solidity memory-safe-assembly
assembly {
let u := calldataload(executionCalldata.offset)
let s := add(executionCalldata.offset, u)
let e := sub(add(executionCalldata.offset, executionCalldata.length), 0x20)
executionBatch.offset := add(s, 0x20)
executionBatch.length := calldataload(s)
if or(shr(64, u), gt(add(s, shl(5, executionBatch.length)), e)) {
mstore(0x00, 0xba597e7e) // `DecodingError()`.
revert(0x1c, 0x04)
}
if executionBatch.length {
// Perform bounds checks on the decoded `executionBatch`.
// Loop runs out-of-gas if `executionBatch.length` is big enough to cause overflows.
for { let i := executionBatch.length } 1 { } {
i := sub(i, 1)
let p := calldataload(add(executionBatch.offset, shl(5, i)))
let c := add(executionBatch.offset, p)
let q := calldataload(add(c, 0x40))
let o := add(c, q)
// forgefmt: disable-next-item
if or(shr(64, or(calldataload(o), or(p, q))),
or(gt(add(c, 0x40), e), gt(add(o, calldataload(o)), e))) {
mstore(0x00, 0xba597e7e) // `DecodingError()`.
revert(0x1c, 0x04)
}
if iszero(i) { break }
}
}
}
}
function encodeBatch(Execution[] memory executions) internal pure returns (bytes memory callData) {
callData = abi.encode(executions);
}
function decodeSingle(bytes calldata executionCalldata)
internal
pure
returns (address target, uint256 value, bytes calldata callData)
{
target = address(bytes20(executionCalldata[0:20]));
value = uint256(bytes32(executionCalldata[20:52]));
callData = executionCalldata[52:];
}
function decodeDelegateCall(bytes calldata executionCalldata)
internal
pure
returns (address delegate, bytes calldata callData)
{
// destructure executionCallData according to single exec
delegate = address(uint160(bytes20(executionCalldata[0:20])));
callData = executionCalldata[20:];
}
function encodeSingle(
address target,
uint256 value,
bytes memory callData
)
internal
pure
returns (bytes memory userOpCalldata)
{
userOpCalldata = abi.encodePacked(target, value, callData);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
// solhint-disable-next-line no-unused-import
import { MODE_MODULE_ENABLE, MODE_PREP, MODE_VALIDATION } from "../../types/Constants.sol";
/**
* Nonce structure
* [3 bytes empty][1 bytes validation mode][20 bytes validator][8 bytes nonce]
*/
library NonceLib {
/// @dev Parses validator address out of nonce
/// @param nonce The nonce
/// @return validator
function getValidator(uint256 nonce) internal pure returns (address validator) {
assembly {
validator := shr(96, shl(32, nonce))
}
}
/// @dev Detects if Validation Mode is Module Enable Mode
/// @param nonce The nonce
/// @return res boolean result, true if it is the Module Enable Mode
function isModuleEnableMode(uint256 nonce) internal pure returns (bool res) {
assembly {
let vmode := byte(3, nonce)
res := eq(shl(248, vmode), MODE_MODULE_ENABLE)
}
}
/// @dev Detects if the validator provided in the nonce is address(0)
/// which means the default validator is used
/// @param nonce The nonce
/// @return res boolean result, true if it is the Default Validator Mode
function isDefaultValidatorMode(uint256 nonce) internal pure returns (bool res) {
assembly {
res := iszero(shr(96, shl(32, nonce)))
}
}
/// @dev Detects if Validation Mode is Prep Mode
/// @param nonce The nonce
/// @return res boolean result, true if it is the Prep Mode
function isPrepMode(uint256 nonce) internal pure returns (bool res) {
assembly {
let vmode := byte(3, nonce)
res := eq(shl(248, vmode), MODE_PREP)
}
}
/// @dev Detects if Validation Mode is Validate Mode
/// @param nonce The nonce
/// @return res boolean result, true if it is the Validation Mode
function isValidateMode(uint256 nonce) internal pure returns (bool res) {
assembly {
let vmode := byte(3, nonce)
res := eq(shl(248, vmode), MODE_VALIDATION)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Sentinel address
address constant SENTINEL = address(0x1);
// Zero address
address constant ZERO_ADDRESS = address(0x0);
/**
* @title SentinelListLib
* @dev Library for managing a linked list of addresses
* @author Rhinestone
*/
library SentinelListLib {
// Struct to hold the linked list
struct SentinelList {
mapping(address => address) entries;
}
error LinkedList_AlreadyInitialized();
error LinkedList_InvalidPage();
error LinkedList_InvalidEntry(address entry);
error LinkedList_EntryAlreadyInList(address entry);
/**
* Initialize the linked list
*
* @param self The linked list
*/
function init(SentinelList storage self) internal {
if (alreadyInitialized(self)) revert LinkedList_AlreadyInitialized();
self.entries[SENTINEL] = SENTINEL;
}
/**
* Check if the linked list is already initialized
*
* @param self The linked list
*
* @return bool True if the linked list is already initialized
*/
function alreadyInitialized(SentinelList storage self) internal view returns (bool) {
return self.entries[SENTINEL] != ZERO_ADDRESS;
}
/**
* Get the next entry in the linked list
*
* @param self The linked list
* @param entry The current entry
*
* @return address The next entry
*/
function getNext(SentinelList storage self, address entry) internal view returns (address) {
if (entry == ZERO_ADDRESS) {
revert LinkedList_InvalidEntry(entry);
}
return self.entries[entry];
}
/**
* Push a new entry to the linked list
*
* @param self The linked list
* @param newEntry The new entry
*/
function push(SentinelList storage self, address newEntry) internal {
if (newEntry == ZERO_ADDRESS || newEntry == SENTINEL) {
revert LinkedList_InvalidEntry(newEntry);
}
if (self.entries[newEntry] != ZERO_ADDRESS) revert LinkedList_EntryAlreadyInList(newEntry);
self.entries[newEntry] = self.entries[SENTINEL];
self.entries[SENTINEL] = newEntry;
}
/**
* Safe push a new entry to the linked list
* @dev This ensures that the linked list is initialized and initializes it if it is not
*
* @param self The linked list
* @param newEntry The new entry
*/
function safePush(SentinelList storage self, address newEntry) internal {
if (!alreadyInitialized({ self: self })) {
init({ self: self });
}
push({ self: self, newEntry: newEntry });
}
/**
* Pop an entry from the linked list
*
* @param self The linked list
* @param prevEntry The entry before the entry to pop
* @param popEntry The entry to pop
*/
function pop(SentinelList storage self, address prevEntry, address popEntry) internal {
if (popEntry == ZERO_ADDRESS || popEntry == SENTINEL) {
revert LinkedList_InvalidEntry(prevEntry);
}
if (self.entries[prevEntry] != popEntry) revert LinkedList_InvalidEntry(popEntry);
self.entries[prevEntry] = self.entries[popEntry];
self.entries[popEntry] = ZERO_ADDRESS;
}
/**
* Pop all entries from the linked list
*
* @param self The linked list
*/
function popAll(SentinelList storage self) internal {
address next = self.entries[SENTINEL];
while (next != ZERO_ADDRESS) {
address current = next;
next = self.entries[next];
self.entries[current] = ZERO_ADDRESS;
}
}
/**
* Check if the linked list contains an entry
*
* @param self The linked list
* @param entry The entry to check
*
* @return bool True if the linked list contains the entry
*/
function contains(SentinelList storage self, address entry) internal view returns (bool) {
return SENTINEL != entry && self.entries[entry] != ZERO_ADDRESS;
}
/**
* Get all entries in the linked list
*
* @param self The linked list
* @param start The start entry
* @param pageSize The page size
*
* @return array All entries in the linked list
* @return next The next entry
*/
function getEntriesPaginated(
SentinelList storage self,
address start,
uint256 pageSize
)
internal
view
returns (address[] memory array, address next)
{
if (start != SENTINEL && !contains(self, start)) revert LinkedList_InvalidEntry(start);
if (pageSize == 0) revert LinkedList_InvalidPage();
// Init array with max page size
array = new address[](pageSize);
// Populate return array
uint256 entryCount = 0;
next = self.entries[start];
while (next != ZERO_ADDRESS && next != SENTINEL && entryCount < pageSize) {
array[entryCount] = next;
next = self.entries[next];
entryCount++;
}
/**
* Because of the argument validation, we can assume that the loop will always iterate over
* the valid entry list values
* and the `next` variable will either be an enabled entry or a sentinel address
* (signalling the end).
*
* If we haven't reached the end inside the loop, we need to set the next pointer to
* the last element of the entry array
* because the `next` variable (which is a entry by itself) acting as a pointer to the
* start of the next page is neither
* incSENTINELrent page, nor will it be included in the next one if you pass it as a
* start.
*/
if (next != SENTINEL && entryCount > 0) {
next = array[entryCount - 1];
}
// Set correct size of returned array
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
mstore(array, entryCount)
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] /// @title Emergency Uninstall /// @notice Struct to encapsulate emergency uninstall data for a hook struct EmergencyUninstall { /// @notice The address of the hook to be uninstalled address hook; /// @notice The hook type identifier uint256 hookType; /// @notice Data used to uninstall the hook bytes deInitData; /// @notice Nonce used to prevent replay attacks uint256 nonce; }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import { EfficientHashLib } from "solady/utils/EfficientHashLib.sol";
import { LibBit } from "solady/utils/LibBit.sol";
import { ECDSA } from "solady/utils/ECDSA.sol";
import { LibEIP7702 } from "solady/accounts/LibEIP7702.sol";
/// @title LibPREP
/// @notice A library to encapsulate the PREP (Provably Rootless EIP-7702 Proxy) workflow.
/// See: https://blog.biconomy.io/prep-deep-dive/
library LibPREP {
/// @dev Validates if `digest` and `saltAndDelegation` results in `target`.
/// `saltAndDelegation` is `bytes32((uint256(salt) << 160) | uint160(delegation))`.
/// Returns a non-zero `r` for the PREP signature, if valid.
/// Otherwise returns 0.
/// `r` will be less than `2**160`, allowing for optional storage packing.
function rPREP(address target, bytes32 digest, bytes32 saltAndDelegation) internal view returns (bytes32 r) {
r = (EfficientHashLib.hash(digest, saltAndDelegation >> 160) << 96) >> 96;
if (!isValid(target, r, address(uint160(uint256(saltAndDelegation))))) r = 0;
}
/// @dev Returns if `r` and `delegation` results in `target`.
function isValid(address target, bytes32 r, address delegation) internal view returns (bool) {
bytes32 s = EfficientHashLib.hash(r);
bytes32 h; // `keccak256(abi.encodePacked(hex"05", LibRLP.p(0).p(delegation).p(0).encode()))`.
assembly ("memory-safe") {
mstore(0x20, 0x80)
mstore(0x1f, delegation)
mstore(0x0b, 0x05d78094)
h := keccak256(0x27, 0x19)
}
return LibBit.and(target != address(0), ECDSA.tryRecover(h, 27, r, s) == target);
}
/// @dev Returns if `target` is a PREP.
function isPREP(address target, bytes32 r) internal view returns (bool) {
address delegation = LibEIP7702.delegationOf(target);
return !LibBit.or(delegation == address(0), r == 0) && isValid(target, r, delegation);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
///
/// @dev Note:
/// - The recovery functions use the ecrecover precompile (0x1).
/// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
/// This is for more safety by default.
/// Use the `tryRecover` variants if you need to get the zero address back
/// upon recovery failure instead.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
/// See: https://eips.ethereum.org/EIPS/eip-2098
/// This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT directly use signatures as unique identifiers:
/// - The recovery operations do NOT check if a signature is non-malleable.
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
/// EIP-712 also enables readable signing of typed data for better user safety.
/// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
library ECDSA {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The order of the secp256k1 elliptic curve.
uint256 internal constant N =
0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;
/// @dev `N/2 + 1`. Used for checking the malleability of the signature.
uint256 private constant _HALF_N_PLUS_1 =
0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The signature is invalid.
error InvalidSignature();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RECOVERY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
for { let m := mload(0x40) } 1 {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
} {
switch mload(signature)
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { continue }
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if returndatasize() { break }
}
}
}
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function recoverCalldata(bytes32 hash, bytes calldata signature)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
for { let m := mload(0x40) } 1 {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
} {
switch signature.length
case 64 {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
}
default { continue }
mstore(0x00, hash)
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if returndatasize() { break }
}
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the EIP-2098 short form signature defined by `r` and `vs`.
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r)
mstore(0x60, shr(1, shl(1, vs))) // `s`.
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(returndatasize()) {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the signature defined by `v`, `r`, `s`.
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, and(v, 0xff))
mstore(0x40, r)
mstore(0x60, s)
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(returndatasize()) {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* TRY-RECOVER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// WARNING!
// These functions will NOT revert upon recovery failure.
// Instead, they will return the zero address upon recovery failure.
// It is critical that the returned address is NEVER compared against
// a zero address (e.g. an uninitialized address variable).
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function tryRecover(bytes32 hash, bytes memory signature)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
for { let m := mload(0x40) } 1 {} {
switch mload(signature)
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
for { let m := mload(0x40) } 1 {} {
switch signature.length
case 64 {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
}
default { break }
mstore(0x00, hash)
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the EIP-2098 short form signature defined by `r` and `vs`.
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r)
mstore(0x60, shr(1, shl(1, vs))) // `s`.
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the signature defined by `v`, `r`, `s`.
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, and(v, 0xff))
mstore(0x40, r)
mstore(0x60, s)
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an Ethereum Signed Message, created from a `hash`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
/// JSON-RPC method as part of EIP-191.
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, hash) // Store into scratch space for keccak256.
mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
}
}
/// @dev Returns an Ethereum Signed Message, created from `s`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
/// JSON-RPC method as part of EIP-191.
/// Note: Supports lengths of `s` up to 999999 bytes.
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let sLength := mload(s)
let o := 0x20
mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
mstore(0x00, 0x00)
// Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
for { let temp := sLength } 1 {} {
o := sub(o, 1)
mstore8(o, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
// Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
mstore(s, sLength) // Restore the length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CANONICAL HASH FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// The following functions return the hash of the signature in its canonicalized format,
// which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
// If `s` is greater than `N / 2` then it will be converted to `N - s`
// and the `v` value will be flipped.
// If the signature has an invalid length, or if `v` is invalid,
// a uniquely corrupt hash will be returned.
// These functions are useful for "poor-mans-VRF".
/// @dev Returns the canonical hash of `signature`.
function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let l := mload(signature)
for {} 1 {} {
mstore(0x00, mload(add(signature, 0x20))) // `r`.
let s := mload(add(signature, 0x40))
let v := mload(add(signature, 0x41))
if eq(l, 64) {
v := add(shr(255, s), 27)
s := shr(1, shl(1, s))
}
if iszero(lt(s, _HALF_N_PLUS_1)) {
v := xor(v, 7)
s := sub(N, s)
}
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
break
}
// If the length is neither 64 nor 65, return a uniquely corrupted hash.
if iszero(lt(sub(l, 64), 2)) {
// `bytes4(keccak256("InvalidSignatureLength"))`.
result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
}
}
}
/// @dev Returns the canonical hash of `signature`.
function canonicalHashCalldata(bytes calldata signature)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
mstore(0x00, calldataload(signature.offset)) // `r`.
let s := calldataload(add(signature.offset, 0x20))
let v := calldataload(add(signature.offset, 0x21))
if eq(signature.length, 64) {
v := add(shr(255, s), 27)
s := shr(1, shl(1, s))
}
if iszero(lt(s, _HALF_N_PLUS_1)) {
v := xor(v, 7)
s := sub(N, s)
}
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
break
}
// If the length is neither 64 nor 65, return a uniquely corrupted hash.
if iszero(lt(sub(signature.length, 64), 2)) {
calldatacopy(mload(0x40), signature.offset, signature.length)
// `bytes4(keccak256("InvalidSignatureLength"))`.
result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2)
}
}
}
/// @dev Returns the canonical hash of `signature`.
function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, r) // `r`.
let v := add(shr(255, vs), 27)
let s := shr(1, shl(1, vs))
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the canonical hash of `signature`.
function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, r) // `r`.
if iszero(lt(s, _HALF_N_PLUS_1)) {
v := xor(v, 7)
s := sub(N, s)
}
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes.
function emptySignature() internal pure returns (bytes calldata signature) {
/// @solidity memory-safe-assembly
assembly {
signature.length := 0
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
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;
uint256 constant MODULE_TYPE_PREVALIDATION_HOOK_ERC1271 = 8;
uint256 constant MODULE_TYPE_PREVALIDATION_HOOK_ERC4337 = 9;
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 { }
interface IPreValidationHookERC1271 is IModule {
function preValidationHookERC1271(
address sender,
bytes32 hash,
bytes calldata data
)
external
view
returns (bytes32 hookHash, bytes memory hookSignature);
}
interface IPreValidationHookERC4337 is IModule {
function preValidationHookERC4337(
PackedUserOperation calldata userOp,
uint256 missingAccountFunds,
bytes32 userOpHash
)
external
returns (bytes32 hookHash, bytes memory hookSignature);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
// STX Sig types
bytes3 constant SIG_TYPE_MEE_FLOW = 0x177eee;
bytes4 constant SIG_TYPE_SIMPLE = 0x177eee00;
bytes4 constant SIG_TYPE_ON_CHAIN = 0x177eee01;
bytes4 constant SIG_TYPE_ERC20_PERMIT = 0x177eee02;
// ...other sig types: ERC-7683, Permit2, etc
// EIP-1271 constants
bytes4 constant ERC1271_SUCCESS = 0x1626ba7e;
bytes4 constant ERC1271_FAILED = 0xffffffff;
// Node PM constants
bytes4 constant NODE_PM_MODE_USER = 0x170de000; // refund goes to the user
bytes4 constant NODE_PM_MODE_DAPP = 0x170de001; // refund goes to the dApp
bytes4 constant NODE_PM_MODE_KEEP = 0x170de002; // no refund as node sponsored
bytes4 constant NODE_PM_PREMIUM_PERCENT = 0x9ee4ce00; // premium percentage
bytes4 constant NODE_PM_PREMIUM_FIXED = 0x9ee4ce01;
// ERC-4337 validation constants
uint256 constant VALIDATION_SUCCESS = 0;
uint256 constant VALIDATION_FAILED = 1;
// Module type identifiers
uint256 constant MODULE_TYPE_MULTI = 0; // Module type identifier for Multitype install
uint256 constant MODULE_TYPE_VALIDATOR = 1;
uint256 constant MODULE_TYPE_EXECUTOR = 2;
uint256 constant MODULE_TYPE_FALLBACK = 3;
uint256 constant MODULE_TYPE_HOOK = 4;
uint256 constant MODULE_TYPE_STATELESS_VALIDATOR = 7;
uint256 constant MODULE_TYPE_PREVALIDATION_HOOK_ERC1271 = 8;
uint256 constant MODULE_TYPE_PREVALIDATION_HOOK_ERC4337 = 9;
// Nexus Validation modes
bytes1 constant MODE_VALIDATION = 0x00;
bytes1 constant MODE_MODULE_ENABLE = 0x01;
bytes1 constant MODE_PREP = 0x02;
// ERC-7739 support constants
bytes4 constant SUPPORTS_ERC7739 = 0x77390000;
bytes4 constant SUPPORTS_ERC7739_V1 = 0x77390001;
// Typehashes
// keccak256("ModuleEnableMode(address module,uint256 moduleType,bytes32 userOpHash,bytes initData)")
bytes32 constant MODULE_ENABLE_MODE_TYPE_HASH = 0xf6c866c1cd985ce61f030431e576c0e82887de0643dfa8a2e6efc3463e638ed0;
// keccak256("EmergencyUninstall(address hook,uint256 hookType,bytes deInitData,uint256 nonce)")
bytes32 constant EMERGENCY_UNINSTALL_TYPE_HASH = 0xd3ddfc12654178cc44d4a7b6b969cfdce7ffe6342326ba37825314cffa0fba9c;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
/// @title ModeLib
/// @author zeroknots.eth | rhinestone.wtf
/// To allow smart accounts to be very simple, but allow for more complex execution, A custom mode
/// encoding is used.
/// Function Signature of execute function:
/// function execute(ExecutionMode mode, bytes calldata executionCalldata) external payable;
/// This allows for a single bytes32 to be used to encode the execution mode, calltype, execType and
/// context.
/// NOTE: Simple Account implementations only have to scope for the most significant byte. Account that
/// implement
/// more complex execution modes may use the entire bytes32.
///
/// |--------------------------------------------------------------------|
/// | CALLTYPE | EXECTYPE | UNUSED | ModeSelector | ModePayload |
/// |--------------------------------------------------------------------|
/// | 1 byte | 1 byte | 4 bytes | 4 bytes | 22 bytes |
/// |--------------------------------------------------------------------|
///
/// CALLTYPE: 1 byte
/// CallType is used to determine how the executeCalldata paramter of the execute function has to be
/// decoded.
/// It can be either single, batch or delegatecall. In the future different calls could be added.
/// CALLTYPE can be used by a validation module to determine how to decode <userOp.callData[36:]>.
///
/// EXECTYPE: 1 byte
/// ExecType is used to determine how the account should handle the execution.
/// It can indicate if the execution should revert on failure or continue execution.
/// In the future more execution modes may be added.
/// Default Behavior (EXECTYPE = 0x00) is to revert on a single failed execution. If one execution in
/// a batch fails, the entire batch is reverted
///
/// UNUSED: 4 bytes
/// Unused bytes are reserved for future use.
///
/// ModeSelector: bytes4
/// The "optional" mode selector can be used by account vendors, to implement custom behavior in
/// their accounts.
/// the way a ModeSelector is to be calculated is bytes4(keccak256("vendorname.featurename"))
/// this is to prevent collisions between different vendors, while allowing innovation and the
/// development of new features without coordination between ERC-7579 implementing accounts
///
/// ModePayload: 22 bytes
/// Mode payload is used to pass additional data to the smart account execution, this may be
/// interpreted depending on the ModeSelector
///
/// ExecutionCallData: n bytes
/// single, delegatecall or batch exec abi.encoded as bytes
// Custom type for improved developer experience
type ExecutionMode is bytes32;
type CallType is bytes1;
type ExecType is bytes1;
type ModeSelector is bytes4;
type ModePayload is bytes22;
// Default CallType
CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
// Batched CallType
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
CallType constant CALLTYPE_STATIC = CallType.wrap(0xFE);
// @dev Implementing delegatecall is OPTIONAL!
// implement delegatecall with extreme care.
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
// @dev default behavior is to revert on failure
// To allow very simple accounts to use mode encoding, the default behavior is to revert on failure
// Since this is value 0x00, no additional encoding is required for simple accounts
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
// @dev account may elect to change execution behavior. For example "try exec" / "allow fail"
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
ModeSelector constant MODE_DEFAULT = ModeSelector.wrap(bytes4(0x00000000));
// Example declaration of a custom mode selector
ModeSelector constant MODE_OFFSET = ModeSelector.wrap(bytes4(keccak256("default.mode.offset")));
/// @dev ModeLib is a helper library to encode/decode ModeCodes
library ModeLib {
function decode(ExecutionMode mode)
internal
pure
returns (CallType _calltype, ExecType _execType, ModeSelector _modeSelector, ModePayload _modePayload)
{
assembly {
_calltype := mode
_execType := shl(8, mode)
_modeSelector := shl(48, mode)
_modePayload := shl(80, mode)
}
}
function decodeBasic(ExecutionMode mode) internal pure returns (CallType _calltype, ExecType _execType) {
assembly {
_calltype := mode
_execType := shl(8, mode)
}
}
function encode(
CallType callType,
ExecType execType,
ModeSelector mode,
ModePayload payload
)
internal
pure
returns (ExecutionMode)
{
return ExecutionMode.wrap(
bytes32(abi.encodePacked(callType, execType, bytes4(0), ModeSelector.unwrap(mode), payload))
);
}
function encodeSimpleBatch() internal pure returns (ExecutionMode mode) {
mode = encode(CALLTYPE_BATCH, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeSimpleSingle() internal pure returns (ExecutionMode mode) {
mode = encode(CALLTYPE_SINGLE, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeTrySingle() internal pure returns (ExecutionMode mode) {
mode = encode(CALLTYPE_SINGLE, EXECTYPE_TRY, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeTryBatch() internal pure returns (ExecutionMode mode) {
mode = encode(CALLTYPE_BATCH, EXECTYPE_TRY, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeCustom(CallType callType, ExecType execType) internal pure returns (ExecutionMode mode) {
mode = encode(callType, execType, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function getCallType(ExecutionMode mode) internal pure returns (CallType calltype) {
assembly {
calltype := mode
}
}
}
using { _eqModeSelector as == } for ModeSelector global;
using { _eqCallType as == } for CallType global;
using { _uneqCallType as != } for CallType global;
using { _eqExecType as == } for ExecType global;
function _eqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) == CallType.unwrap(b);
}
function _uneqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) != CallType.unwrap(b);
}
function _eqExecType(ExecType a, ExecType b) pure returns (bool) {
return ExecType.unwrap(a) == ExecType.unwrap(b);
}
//slither-disable-next-line dead-code
function _eqModeSelector(ModeSelector a, ModeSelector b) pure returns (bool) {
return ModeSelector.unwrap(a) == ModeSelector.unwrap(b);
}// 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; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IBaseAccountEventsAndErrors } from "./IBaseAccountEventsAndErrors.sol"; /// @title Nexus - IBaseAccount /// @notice Interface for the BaseAccount functionalities compliant with ERC-7579 and ERC-4337. /// @dev Interface for organizing the base functionalities using the Nexus suite. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IBaseAccount is IBaseAccountEventsAndErrors { /// @notice Adds deposit to the EntryPoint to fund transactions. function addDeposit() external payable; /// @notice Withdraws ETH from the EntryPoint to a specified address. /// @param to The address to receive the withdrawn funds. /// @param amount The amount to withdraw. function withdrawDepositTo(address to, uint256 amount) external payable; /// @notice Returns the current deposit balance of this account on the EntryPoint. /// @return The current balance held at the EntryPoint. function getDeposit() external view returns (uint256); /// @notice Retrieves the address of the EntryPoint contract, currently using version 0.7. /// @return The address of the EntryPoint contract. function entryPoint() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IStorage } from "../../interfaces/nexus/base/IStorage.sol"; /// @title Nexus - Storage /// @notice Manages isolated storage spaces for Modular Smart Account in compliance with ERC-7201 standard to ensure /// collision-resistant storage. /// @dev Implements the ERC-7201 namespaced storage pattern to maintain secure and isolated storage sections for /// different /// states within Nexus suite. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady contract Storage is IStorage { /// @custom:storage-location erc7201:biconomy.storage.Nexus /// ERC-7201 namespaced via `keccak256(abi.encode(uint256(keccak256(bytes("biconomy.storage.Nexus"))) - 1)) & /// ~bytes32(uint256(0xff));` bytes32 private constant _STORAGE_LOCATION = 0x0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f00; /// @dev Utilizes ERC-7201's namespaced storage pattern for isolated storage access. This method computes /// the storage slot based on a predetermined location, ensuring collision-resistant storage for contract states. /// @custom:storage-location ERC-7201 formula applied to "biconomy.storage.Nexus", facilitating unique /// namespace identification and storage segregation, as detailed in the specification. /// @return $ The proxy to the `AccountStorage` struct, providing a reference to the namespaced storage slot. function _getAccountStorage() internal pure returns (AccountStorage storage $) { assembly { $.slot := _STORAGE_LOCATION } } }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
library LocalCallDataParserLib {
/// @dev Parses the `userOp.signature` to extract the module type, module initialization data,
/// enable mode signature, and user operation signature. The `userOp.signature` must be
/// encoded in a specific way to be parsed correctly.
/// @param packedData The packed signature data, typically coming from `userOp.signature`.
/// @return module The address of the module.
/// @return moduleType The type of module as a `uint256`.
/// @return moduleInitData Initialization data specific to the module.
/// @return enableModeSignature Signature used to enable the module mode.
/// @return userOpSignature The remaining user operation signature data.
function parseEnableModeData(bytes calldata packedData)
internal
pure
returns (
address module,
uint256 moduleType,
bytes calldata moduleInitData,
bytes calldata enableModeSignature,
bytes calldata userOpSignature
)
{
uint256 p;
assembly ("memory-safe") {
p := packedData.offset
module := shr(96, calldataload(p))
p := add(p, 0x14)
moduleType := calldataload(p)
moduleInitData.length := shr(224, calldataload(add(p, 0x20)))
moduleInitData.offset := add(p, 0x24)
p := add(moduleInitData.offset, moduleInitData.length)
enableModeSignature.length := shr(224, calldataload(p))
enableModeSignature.offset := add(p, 0x04)
p := sub(add(enableModeSignature.offset, enableModeSignature.length), packedData.offset)
}
userOpSignature = packedData[p:];
}
/// @dev Parses the data to obtain types and initdata's for Multi Type module install mode
/// @param initData Multi Type module init data, abi.encoded
function parseMultiTypeInitData(bytes calldata initData)
internal
pure
returns (uint256[] calldata types, bytes[] calldata initDatas)
{
// equivalent of:
// (types, initDatas) = abi.decode(initData,(uint[],bytes[]))
assembly ("memory-safe") {
let offset := initData.offset
let baseOffset := offset
let dataPointer := add(baseOffset, calldataload(offset))
types.offset := add(dataPointer, 32)
types.length := calldataload(dataPointer)
offset := add(offset, 32)
dataPointer := add(baseOffset, calldataload(offset))
initDatas.offset := add(dataPointer, 32)
initDatas.length := calldataload(dataPointer)
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IModuleManagerEventsAndErrors } from "./IModuleManagerEventsAndErrors.sol"; /// @title Nexus - IModuleManager /// @notice Interface for managing modules within Smart Accounts, providing methods for installation and removal of /// modules. /// @dev Extends the IModuleManagerEventsAndErrors interface to include event and error definitions. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IModuleManager is IModuleManagerEventsAndErrors { /// @notice Installs a Module of a specific type onto the smart account. /// @param moduleTypeId The identifier for the module type. /// @param module The address of the module to be installed. /// @param initData Initialization data for configuring the module upon installation. function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external payable; /// @notice Uninstalls a Module of a specific type from the smart account. /// @param moduleTypeId The identifier for the module type being uninstalled. /// @param module The address of the module to uninstall. /// @param deInitData De-initialization data for configuring the module upon uninstallation. function uninstallModule(uint256 moduleTypeId, address module, bytes calldata deInitData) external payable; /// @notice Checks if a specific module is installed on the smart account. /// @param moduleTypeId The module type identifier to check. /// @param module The address of the module. /// @param additionalContext Additional information that may be required to verify the module's installation. /// @return installed True if the module is installed, false otherwise. function isModuleInstalled( uint256 moduleTypeId, address module, bytes calldata additionalContext ) external view returns (bool installed); }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS AND IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 internal constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
/// This is only used in `_hashTypedDataSansChainId`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;
/// @dev `keccak256("EIP712Domain(string name,string version)")`.
/// This is only used in `_hashTypedDataSansChainIdAndVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT =
0xb03948446334eb9b2196d5eb166f69b9d49403eb4a12f36de8d3f9f3cb8e15c3;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId)")`.
/// This is only used in `_hashTypedDataSansVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT =
0xc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e;
uint256 private immutable _cachedThis;
uint256 private immutable _cachedChainId;
bytes32 private immutable _cachedNameHash;
bytes32 private immutable _cachedVersionHash;
bytes32 private immutable _cachedDomainSeparator;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Cache the hashes for cheaper runtime gas costs.
/// In the case of upgradeable contracts (i.e. proxies),
/// or if the chain id changes due to a hard fork,
/// the domain separator will be seamlessly calculated on-the-fly.
constructor() {
_cachedThis = uint256(uint160(address(this)));
_cachedChainId = block.chainid;
string memory name;
string memory version;
if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
bytes32 versionHash =
_domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
_cachedNameHash = nameHash;
_cachedVersionHash = versionHash;
bytes32 separator;
if (!_domainNameAndVersionMayChange()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
_cachedDomainSeparator = separator;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to return the domain name and version.
/// ```
/// function _domainNameAndVersion()
/// internal
/// pure
/// virtual
/// returns (string memory name, string memory version)
/// {
/// name = "Solady";
/// version = "1";
/// }
/// ```
///
/// Note: If the returned result may change after the contract has been deployed,
/// you must override `_domainNameAndVersionMayChange()` to return true.
function _domainNameAndVersion()
internal
view
virtual
returns (string memory name, string memory version);
/// @dev Returns if `_domainNameAndVersion()` may change
/// after the contract has been deployed (i.e. after the constructor).
/// Default: false.
function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _domainSeparator() internal view virtual returns (bytes32 separator) {
if (_domainNameAndVersionMayChange()) {
separator = _buildDomainSeparator();
} else {
separator = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
}
}
/// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
/// given `structHash`, as defined in
/// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
///
/// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
/// ```
/// bytes32 digest = _hashTypedData(keccak256(abi.encode(
/// keccak256("Mail(address to,string contents)"),
/// mailTo,
/// keccak256(bytes(mailContents))
/// )));
/// address signer = ECDSA.recover(digest, signature);
/// ```
function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
// We will use `digest` to store the domain separator to save a bit of gas.
if (_domainNameAndVersionMayChange()) {
digest = _buildDomainSeparator();
} else {
digest = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
}
/// @solidity memory-safe-assembly
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, digest) // Store the domain separator.
mstore(0x3a, structHash) // Store the struct hash.
digest := keccak256(0x18, 0x42)
// Restore the part of the free memory slot that was overwritten.
mstore(0x3a, 0)
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID.
/// Included for the niche use case of cross-chain workflows.
function _hashTypedDataSansChainId(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, address())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of cross-chain and multi-verifier workflows.
function _hashTypedDataSansChainIdAndVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x60)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of multi-verifier workflows.
function _hashTypedDataSansVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, chainid())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-5267 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev See: https://eips.ethereum.org/EIPS/eip-5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b01111`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
salt = salt; // `bytes32(0)`.
extensions = extensions; // `new uint256[](0)`.
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _buildDomainSeparator() private view returns (bytes32 separator) {
// We will use `separator` to store the name hash to save a bit of gas.
bytes32 versionHash;
if (_domainNameAndVersionMayChange()) {
(string memory name, string memory version) = _domainNameAndVersion();
separator = keccak256(bytes(name));
versionHash = keccak256(bytes(version));
} else {
separator = _cachedNameHash;
versionHash = _cachedVersionHash;
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
/// @dev Returns if the cached domain separator has been invalidated.
function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
uint256 cachedChainId = _cachedChainId;
uint256 cachedThis = _cachedThis;
/// @solidity memory-safe-assembly
assembly {
result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
}
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.7.6;
library ExcessivelySafeCall {
uint256 constant LOW_28_MASK =
0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _value The value in wei to send to the remote contract
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeCall(
address _target,
uint256 _gas,
uint256 _value,
uint16 _maxCopy,
bytes memory _calldata
) internal returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid memcopying a very large returndata
// returned by a malicious contract
assembly {
_success := call(
_gas, // gas
_target, // recipient
_value, // ether value
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returnData, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeStaticCall(
address _target,
uint256 _gas,
uint16 _maxCopy,
bytes memory _calldata
) internal view returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid memcopying a very large returndata
// returned by a malicious contract
assembly {
_success := staticcall(
_gas, // gas
_target, // recipient
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returnData, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
/**
* @notice Swaps function selectors in encoded contract calls
* @dev Allows reuse of encoded calldata for functions with identical
* argument types but different names. It simply swaps out the first 4 bytes
* for the new selector. This function modifies memory in place, and should
* only be used with caution.
* @param _newSelector The new 4-byte selector
* @param _buf The encoded contract args
*/
function swapSelector(bytes4 _newSelector, bytes memory _buf)
internal
pure
{
require(_buf.length >= 4);
uint256 _mask = LOW_28_MASK;
assembly {
// load the first word of
let _word := mload(add(_buf, 0x20))
// mask out the top 4 bytes
// /x
_word := and(_word, _mask)
_word := or(_newSelector, _word)
mstore(add(_buf, 0x20), _word)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import { CallType, ExecType, ModeCode } from "../lib/ModeLib.sol";
struct Execution {
address target;
uint256 value;
bytes callData;
}
interface IERC7579Account {
event ModuleInstalled(uint256 moduleTypeId, address module);
event ModuleUninstalled(uint256 moduleTypeId, address module);
/**
* @dev Executes a transaction on behalf of the account.
* This function is intended to be called by ERC-4337 EntryPoint.sol
* @dev Ensure adequate authorization control: i.e. onlyEntryPointOrSelf
*
* @dev MSA MUST implement this function signature.
* If a mode is requested that is not supported by the Account, it MUST revert
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*/
function execute(ModeCode mode, bytes calldata executionCalldata) external payable;
/**
* @dev Executes a transaction on behalf of the account.
* This function is intended to be called by Executor Modules
* @dev Ensure adequate authorization control: i.e. onlyExecutorModule
*
* @dev MSA MUST implement this function signature.
* If a mode is requested that is not supported by the Account, it MUST revert
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*/
function executeFromExecutor(
ModeCode mode,
bytes calldata executionCalldata
)
external
payable
returns (bytes[] memory returnData);
/**
* @dev ERC-1271 isValidSignature
* This function is intended to be used to validate a smart account signature
* and may forward the call to a validator module
*
* @param hash The hash of the data that is signed
* @param data The data that is signed
*/
function isValidSignature(bytes32 hash, bytes calldata data) external view returns (bytes4);
/**
* @dev installs a Module of a certain type on the smart account
* @dev Implement Authorization control of your chosing
* @param moduleTypeId the module type ID according the ERC-7579 spec
* @param module the module address
* @param initData arbitrary data that may be required on the module during `onInstall`
* initialization.
*/
function installModule(
uint256 moduleTypeId,
address module,
bytes calldata initData
)
external
payable;
/**
* @dev uninstalls a Module of a certain type on the smart account
* @dev Implement Authorization control of your chosing
* @param moduleTypeId the module type ID according the ERC-7579 spec
* @param module the module address
* @param deInitData arbitrary data that may be required on the module during `onUninstall`
* de-initialization.
*/
function uninstallModule(
uint256 moduleTypeId,
address module,
bytes calldata deInitData
)
external
payable;
/**
* Function to check if the account supports a certain CallType or ExecType (see ModeLib.sol)
* @param encodedMode the encoded mode
*/
function supportsExecutionMode(ModeCode encodedMode) external view returns (bool);
/**
* Function to check if the account supports installation of a certain module type Id
* @param moduleTypeId the module type ID according the ERC-7579 spec
*/
function supportsModule(uint256 moduleTypeId) external view returns (bool);
/**
* Function to check if the account has a certain module installed
* @param moduleTypeId the module type ID according the ERC-7579 spec
* Note: keep in mind that some contracts can be multiple module types at the same time. It
* thus may be necessary to query multiple module types
* @param module the module address
* @param additionalContext additional context data that the smart account may interpret to
* identifiy conditions under which the module is installed.
* usually this is not necessary, but for some special hooks that
* are stored in mappings, this param might be needed
*/
function isModuleInstalled(
uint256 moduleTypeId,
address module,
bytes calldata additionalContext
)
external
view
returns (bool);
/**
* @dev Returns the account id of the smart account
* @return accountImplementationId the account id of the smart account
* the accountId should be structured like so:
* "vendorname.accountname.semver"
*/
function accountId() external view returns (string memory accountImplementationId);
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { ExecutionMode } from "../../../lib/erc-7579/ModeLib.sol"; import { IExecutionHelperEventsAndErrors } from "./IExecutionHelperEventsAndErrors.sol"; import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; /// @title Nexus - IExecutionHelper /// @notice Interface for executing transactions on behalf of smart accounts within the Nexus system. /// @dev Extends functionality for transaction execution with error handling as defined in /// IExecutionHelperEventsAndErrors. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IExecutionHelper is IExecutionHelperEventsAndErrors { /// @notice Executes a transaction with specified execution mode and calldata. /// @param mode The execution mode, defining how the transaction is processed. /// @param executionCalldata The calldata to execute. /// @dev This function ensures that the execution complies with smart account execution policies and handles errors /// appropriately. function execute(ExecutionMode mode, bytes calldata executionCalldata) external payable; /// @notice Allows an executor module to perform transactions on behalf of the account. /// @param mode The execution mode that details how the transaction should be handled. /// @param executionCalldata The transaction data to be executed. /// @return returnData The result of the execution, allowing for error handling and results interpretation by the /// executor module. function executeFromExecutor( ExecutionMode mode, bytes calldata executionCalldata ) external payable returns (bytes[] memory returnData); /** * Account may implement this execute method. * passing this methodSig at the beginning of callData will cause the entryPoint to pass the full UserOp (and hash) * to the account. * The account should skip the methodSig, and use the callData (and optionally, other UserOp fields) * * @param userOp - The operation that was just validated. * @param userOpHash - Hash of the user's request data. */ function executeUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external payable; }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import { ComposableStorage } from "./ComposableStorage.sol";
import {
InputParam,
OutputParam,
Constraint,
ConstraintType,
InputParamType,
InputParamFetcherType,
OutputParamFetcherType
} from "../types/ComposabilityDataTypes.sol";
import { Execution } from "erc7579/interfaces/IERC7579Account.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// Library for composable execution handling
library ComposableExecutionLib {
error ConstraintNotMet(ConstraintType constraintType);
error Output_StaticCallFailed();
error InvalidParameterEncoding(string message);
error InvalidOutputParamFetcherType();
error InvalidConstraintType();
error InvalidSetOfInputParams(string message);
// Process the input parameters and return the composed calldata
function processInputs(
InputParam[] calldata inputParams,
bytes4 functionSig
)
internal
view
returns (Execution memory)
{
address composedTarget;
uint256 composedValue;
bytes memory composedCalldata = abi.encodePacked(functionSig);
uint256 length = inputParams.length;
// Bit 0: TARGET param type set, Bit 1: VALUE param type set
uint256 paramTypeFlags = 0;
for (uint256 i; i < length; i++) {
bytes memory processedInput = processInput(inputParams[i]);
if (inputParams[i].paramType == InputParamType.TARGET) {
if (inputParams[i].fetcherType == InputParamFetcherType.BALANCE) {
revert InvalidParameterEncoding("BALANCE fetcher type is not supported for TARGET param type");
}
// Check if TARGET has already been set (bit 0)
if (paramTypeFlags & 1 != 0) {
revert InvalidSetOfInputParams("TARGET param type can only be set once");
}
paramTypeFlags |= 1; // Set bit 0
composedTarget = abi.decode(processedInput, (address));
} else if (inputParams[i].paramType == InputParamType.VALUE) {
// Check if VALUE has already been set (bit 1)
if (paramTypeFlags & 2 != 0) {
revert InvalidSetOfInputParams("VALUE param type can only be set once");
}
paramTypeFlags |= 2; // Set bit 1
composedValue = abi.decode(processedInput, (uint256));
} else if (inputParams[i].paramType == InputParamType.CALL_DATA) {
composedCalldata = bytes.concat(composedCalldata, processedInput);
} else {
revert InvalidParameterEncoding("Invalid param type");
}
}
// if a param with TARGET type was not provided, it will be address(0)
// we don't restrict it since some calls may want to call address(0)
// if a param with VALUE type was not provided, it will be 0
// this is even more often case, as many calls happen with 0 value
return Execution({ target: composedTarget, value: composedValue, callData: composedCalldata });
}
// Process a single input parameter and return the composed calldata
function processInput(InputParam calldata param) internal view returns (bytes memory) {
if (param.fetcherType == InputParamFetcherType.RAW_BYTES) {
_validateConstraints(param.paramData, param.constraints);
return param.paramData;
} else if (param.fetcherType == InputParamFetcherType.STATIC_CALL) {
address contractAddr;
bytes calldata callData;
bytes calldata paramData = param.paramData;
// expect paramData to be abi.encode(address contractAddr, bytes callData)
assembly {
contractAddr := calldataload(paramData.offset)
let s := calldataload(add(paramData.offset, 0x20))
let u := add(paramData.offset, s)
callData.offset := add(u, 0x20)
callData.length := calldataload(u)
}
(bool success, bytes memory returnData) = contractAddr.staticcall(callData);
assembly {
if iszero(success) {
// revert ComposableExecutionFailed()
mstore(0x00, 0x6533cc8d)
revert(0x1c, 0x04)
}
}
_validateConstraints(returnData, param.constraints);
return returnData;
} else if (param.fetcherType == InputParamFetcherType.BALANCE) {
address tokenAddr;
address account;
bytes calldata paramData = param.paramData;
// expect paramData to be abi.encodePacked(address token, address account)
// Validate exact length requirement
require(paramData.length == 40, InvalidParameterEncoding("Invalid paramData length"));
assembly {
tokenAddr := shr(96, calldataload(paramData.offset))
account := shr(96, calldataload(add(paramData.offset, 0x14)))
}
uint256 balance;
if (tokenAddr == address(0)) {
balance = account.balance;
} else {
balance = IERC20(tokenAddr).balanceOf(account);
}
_validateConstraints(abi.encode(balance), param.constraints);
return abi.encode(balance);
} else {
revert InvalidParameterEncoding("Invalid param fetcher type");
}
}
// Process the output parameters
function processOutputs(OutputParam[] calldata outputParams, bytes memory returnData, address account) internal {
uint256 length = outputParams.length;
for (uint256 i; i < length; i++) {
processOutput(outputParams[i], returnData, account);
}
}
// Process a single output parameter and write to storage
function processOutput(OutputParam calldata param, bytes memory returnData, address account) internal {
// only static types are supported for now as return values
// can also process all the static return values which are before the first dynamic return value in the
// returnData
if (param.fetcherType == OutputParamFetcherType.EXEC_RESULT) {
uint256 returnValues;
address targetStorageContract;
bytes32 targetStorageSlot;
bytes calldata paramData = param.paramData;
assembly {
returnValues := calldataload(paramData.offset)
targetStorageContract := calldataload(add(paramData.offset, 0x20))
targetStorageSlot := calldataload(add(paramData.offset, 0x40))
}
_parseReturnDataAndWriteToStorage(
returnValues, returnData, targetStorageContract, targetStorageSlot, account
);
// same for static calls
} else if (param.fetcherType == OutputParamFetcherType.STATIC_CALL) {
uint256 returnValues;
address sourceContract;
bytes calldata sourceCallData;
address targetStorageContract;
bytes32 targetStorageSlot;
bytes calldata paramData = param.paramData;
assembly {
returnValues := calldataload(paramData.offset)
sourceContract := calldataload(add(paramData.offset, 0x20))
let s := calldataload(add(paramData.offset, 0x40))
let u := add(paramData.offset, s)
sourceCallData.offset := add(u, 0x20)
sourceCallData.length := calldataload(u)
targetStorageContract := calldataload(add(paramData.offset, 0x60))
targetStorageSlot := calldataload(add(paramData.offset, 0x80))
}
(bool outputSuccess, bytes memory outputReturnData) = sourceContract.staticcall(sourceCallData);
if (!outputSuccess) {
revert Output_StaticCallFailed();
}
_parseReturnDataAndWriteToStorage(
returnValues, outputReturnData, targetStorageContract, targetStorageSlot, account
);
} else {
revert InvalidOutputParamFetcherType();
}
}
/// @dev Validate the constraints => compare the value with the reference data
function _validateConstraints(bytes memory rawValue, Constraint[] calldata constraints) private pure {
if (constraints.length > 0) {
for (uint256 i; i < constraints.length; i++) {
Constraint memory constraint = constraints[i];
bytes32 returnValue;
assembly {
returnValue := mload(add(rawValue, add(0x20, mul(i, 0x20))))
}
if (constraint.constraintType == ConstraintType.EQ) {
require(returnValue == bytes32(constraint.referenceData), ConstraintNotMet(ConstraintType.EQ));
} else if (constraint.constraintType == ConstraintType.GTE) {
require(returnValue >= bytes32(constraint.referenceData), ConstraintNotMet(ConstraintType.GTE));
} else if (constraint.constraintType == ConstraintType.LTE) {
require(returnValue <= bytes32(constraint.referenceData), ConstraintNotMet(ConstraintType.LTE));
} else if (constraint.constraintType == ConstraintType.IN) {
(bytes32 lowerBound, bytes32 upperBound) = abi.decode(constraint.referenceData, (bytes32, bytes32));
require(returnValue >= lowerBound && returnValue <= upperBound, ConstraintNotMet(ConstraintType.IN));
} else {
revert InvalidConstraintType();
}
}
}
}
/// @dev Parse the return data and write to the appropriate storage contract
function _parseReturnDataAndWriteToStorage(
uint256 returnValues,
bytes memory returnData,
address targetStorageContract,
bytes32 targetStorageSlot,
address account
)
internal
{
for (uint256 i; i < returnValues; i++) {
bytes32 value;
assembly {
value := mload(add(returnData, add(0x20, mul(i, 0x20))))
}
ComposableStorage(targetStorageContract)
.writeStorage({
slot: keccak256(abi.encodePacked(targetStorageSlot, i)), value: value, account: account
});
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
// Type of the input parameter
enum InputParamType {
TARGET, // The target address
VALUE, // The value
CALL_DATA // The call data
}
// Parameter type for composition
enum InputParamFetcherType {
RAW_BYTES, // Already encoded bytes
STATIC_CALL, // Perform a static call
BALANCE // Get the balance of an address
}
enum OutputParamFetcherType {
EXEC_RESULT, // The return of the execution call
STATIC_CALL // Call to some other function
}
// Constraint type for parameter validation
enum ConstraintType {
EQ, // Equal to
GTE, // Greater than or equal to
LTE, // Less than or equal to
IN // In range
}
// Constraint for parameter validation
struct Constraint {
ConstraintType constraintType;
bytes referenceData;
}
// Structure to define parameter composition
struct InputParam {
InputParamType paramType;
InputParamFetcherType fetcherType; // How to fetch the parameter
bytes paramData;
Constraint[] constraints;
}
// Structure to define return value handling
struct OutputParam {
OutputParamFetcherType fetcherType; // How to fetch the parameter
bytes paramData;
}
// Structure to define a composable execution
struct ComposableExecution {
bytes4 functionSig;
InputParam[] inputParams;
OutputParam[] outputParams;
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.23;
import { ComposableExecution } from "../types/ComposabilityDataTypes.sol";
interface IComposableExecution {
function executeComposable(ComposableExecution[] calldata cExecutions) external payable;
}
interface IComposableExecutionModule is IComposableExecution {
function executeComposableCall(ComposableExecution[] calldata cExecutions) external;
function executeComposableDelegateCall(ComposableExecution[] calldata cExecutions) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Call context checker mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/CallContextChecker.sol)
contract CallContextChecker {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The call is from an unauthorized call context.
error UnauthorizedCallContext();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For checking if the context is a delegate call.
///
/// Note: To enable use cases with an immutable default implementation in the bytecode,
/// (see: ERC6551Proxy), we don't require that the proxy address must match the
/// value stored in the implementation slot, which may not be initialized.
uint256 private immutable __self = uint256(uint160(address(this)));
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CALL CONTEXT CHECKS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A proxy call can be either via a `delegatecall` to an implementation,
// or a 7702 call on an authority that points to a delegation.
/// @dev Returns whether the current call context is on a EIP7702 authority
/// (i.e. externally owned account).
function _onEIP7702Authority() internal view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
extcodecopy(address(), 0x00, 0x00, 0x20)
// Note: Checking that it starts with hex"ef01" is the most general and futureproof.
// 7702 bytecode is `abi.encodePacked(hex"ef01", uint8(version), address(delegation))`.
result := eq(0xef01, shr(240, mload(0x00)))
}
}
/// @dev Returns the implementation of this contract.
function _selfImplementation() internal view virtual returns (address) {
return address(uint160(__self));
}
/// @dev Returns whether the current call context is on the implementation itself.
function _onImplementation() internal view virtual returns (bool) {
return __self == uint160(address(this));
}
/// @dev Requires that the current call context is performed via a EIP7702 authority.
function _checkOnlyEIP7702Authority() internal view virtual {
if (!_onEIP7702Authority()) _revertUnauthorizedCallContext();
}
/// @dev Requires that the current call context is performed via a proxy.
function _checkOnlyProxy() internal view virtual {
if (_onImplementation()) _revertUnauthorizedCallContext();
}
/// @dev Requires that the current call context is NOT performed via a proxy.
/// This is the opposite of `checkOnlyProxy`.
function _checkNotDelegated() internal view virtual {
if (!_onImplementation()) _revertUnauthorizedCallContext();
}
/// @dev Requires that the current call context is performed via a EIP7702 authority.
modifier onlyEIP7702Authority() virtual {
_checkOnlyEIP7702Authority();
_;
}
/// @dev Requires that the current call context is performed via a proxy.
modifier onlyProxy() virtual {
_checkOnlyProxy();
_;
}
/// @dev Requires that the current call context is NOT performed via a proxy.
/// This is the opposite of `onlyProxy`.
modifier notDelegated() virtual {
_checkNotDelegated();
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _revertUnauthorizedCallContext() private pure {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
revert(0x1c, 0x04)
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { ExecutionMode } from "../../../lib/erc-7579/ModeLib.sol"; /// @title Nexus - ERC-7579 Account Configuration Interface /// @notice Interface for querying and verifying configurations of Smart Accounts compliant with ERC-7579. /// @dev Provides methods to check supported execution modes and module types for Smart Accounts, ensuring flexible and /// extensible configuration. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IAccountConfig { /// @notice Returns the account ID in a structured format: "vendorname.accountname.semver" /// @return accountImplementationId The account ID of the smart account function accountId() external view returns (string memory accountImplementationId); /// @notice Checks if the account supports a certain execution mode. /// @param encodedMode The encoded mode to verify. /// @return supported True if the account supports the mode, false otherwise. function supportsExecutionMode(ExecutionMode encodedMode) external view returns (bool supported); /// @notice Checks if the account supports a specific module type. /// @param moduleTypeId The module type ID to verify. /// @return supported True if the account supports the module type, false otherwise. function supportsModule(uint256 moduleTypeId) external view returns (bool supported); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] /// @title Nexus - INexus Events and Errors /// @notice Defines common errors for the Nexus smart account management interface. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface INexusEventsAndErrors { /// @notice Error thrown when an unsupported ModuleType is requested. /// @param moduleTypeId The ID of the unsupported module type. error UnsupportedModuleType(uint256 moduleTypeId); /// @notice Error thrown when a zero address is provided as the Entry Point address. error EntryPointCanNotBeZero(); /// @notice Error thrown when the provided implementation address is invalid. error InvalidImplementationAddress(); /// @notice Error thrown when attempted to emergency-uninstall a hook error EmergencyTimeLockNotExpired(); /// @notice Error thrown when attempted to upgrade an ERC7702 account via UUPS proxy upgrade mechanism error ERC7702AccountCannotBeUpgradedThisWay(); /// @notice Error thrown when the provided initData is invalid. error InvalidInitData(); /// @notice Error thrown when the account is already initialized. error AccountAlreadyInitialized(); /// @notice Error thrown when the account is not initialized but expected to be. error AccountNotInitialized(); /// @notice Error thrown when the provided signature is invalid. error InvalidSignature(); }
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.27;
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param _dataHash Arbitrary length data signed on behalf of address(this)
* @param _signature Signature byte array associated with _data
*
* MUST return the bytes4 magic value 0x1626ba7e when function passes.
* MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc >
* 0.5)
* MUST allow external calls
*/
function isValidSignature(bytes32 _dataHash, bytes calldata _signature) external view returns (bytes4);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
import "./PackedUserOperation.sol";
interface IAccount {
/**
* Validate user's signature and nonce
* the entryPoint will make the call to the recipient only if this validation call returns successfully.
* signature failure should be reported by returning SIG_VALIDATION_FAILED (1).
* This allows making a "simulation call" without a valid signature
* Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure.
*
* @dev Must validate caller is the entryPoint.
* Must validate the signature and nonce
* @param userOp - The operation that is about to be executed.
* @param userOpHash - Hash of the user's request data. can be used as the basis for signature.
* @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint.
* This is the minimum amount to transfer to the sender(entryPoint) to be
* able to make the call. The excess is left as a deposit in the entrypoint
* for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()".
* In case there is a paymaster in the request (or the current deposit is high
* enough), this value will be zero.
* @return validationData - Packaged ValidationData structure. use `_packValidationData` and
* `_unpackValidationData` to encode and decode.
* <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
* otherwise, an address of an "authorizer" contract.
* <6-byte> validUntil - Last timestamp this operation is valid. 0 for "indefinite"
* <6-byte> validAfter - First timestamp this operation is valid
* If an account doesn't use time-range, it is enough to
* return SIG_VALIDATION_FAILED value (1) for signature failure.
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for efficiently performing keccak256 hashes.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EfficientHashLib.sol)
/// @dev To avoid stack-too-deep, you can use:
/// ```
/// bytes32[] memory buffer = EfficientHashLib.malloc(10);
/// EfficientHashLib.set(buffer, 0, value0);
/// ..
/// EfficientHashLib.set(buffer, 9, value9);
/// bytes32 finalHash = EfficientHashLib.hash(buffer);
/// ```
library EfficientHashLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MALLOC-LESS HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `keccak256(abi.encode(v0))`.
function hash(bytes32 v0) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
result := keccak256(0x00, 0x20)
}
}
/// @dev Returns `keccak256(abi.encode(v0))`.
function hash(uint256 v0) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
result := keccak256(0x00, 0x20)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1))`.
function hash(bytes32 v0, bytes32 v1) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
mstore(0x20, v1)
result := keccak256(0x00, 0x40)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1))`.
function hash(uint256 v0, uint256 v1) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, v0)
mstore(0x20, v1)
result := keccak256(0x00, 0x40)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
result := keccak256(m, 0x60)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
function hash(uint256 v0, uint256 v1, uint256 v2) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
result := keccak256(m, 0x60)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
result := keccak256(m, 0x80)
}
}
/// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
result := keccak256(m, 0x80)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
result := keccak256(m, 0xa0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
result := keccak256(m, 0xa0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4, bytes32 v5)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
result := keccak256(m, 0xc0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4, uint256 v5)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
result := keccak256(m, 0xc0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
result := keccak256(m, 0xe0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
result := keccak256(m, 0xe0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
result := keccak256(m, 0x100)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
result := keccak256(m, 0x100)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
result := keccak256(m, 0x120)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
result := keccak256(m, 0x120)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
result := keccak256(m, 0x140)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
result := keccak256(m, 0x140)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
result := keccak256(m, 0x160)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
result := keccak256(m, 0x160)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10,
bytes32 v11
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
result := keccak256(m, 0x180)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10,
uint256 v11
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
result := keccak256(m, 0x180)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10,
bytes32 v11,
bytes32 v12
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
result := keccak256(m, 0x1a0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10,
uint256 v11,
uint256 v12
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
result := keccak256(m, 0x1a0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
function hash(
bytes32 v0,
bytes32 v1,
bytes32 v2,
bytes32 v3,
bytes32 v4,
bytes32 v5,
bytes32 v6,
bytes32 v7,
bytes32 v8,
bytes32 v9,
bytes32 v10,
bytes32 v11,
bytes32 v12,
bytes32 v13
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
mstore(add(m, 0x1a0), v13)
result := keccak256(m, 0x1c0)
}
}
/// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
function hash(
uint256 v0,
uint256 v1,
uint256 v2,
uint256 v3,
uint256 v4,
uint256 v5,
uint256 v6,
uint256 v7,
uint256 v8,
uint256 v9,
uint256 v10,
uint256 v11,
uint256 v12,
uint256 v13
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, v0)
mstore(add(m, 0x20), v1)
mstore(add(m, 0x40), v2)
mstore(add(m, 0x60), v3)
mstore(add(m, 0x80), v4)
mstore(add(m, 0xa0), v5)
mstore(add(m, 0xc0), v6)
mstore(add(m, 0xe0), v7)
mstore(add(m, 0x100), v8)
mstore(add(m, 0x120), v9)
mstore(add(m, 0x140), v10)
mstore(add(m, 0x160), v11)
mstore(add(m, 0x180), v12)
mstore(add(m, 0x1a0), v13)
result := keccak256(m, 0x1c0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTES32 BUFFER HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `keccak256(abi.encode(buffer[0], .., buffer[buffer.length - 1]))`.
function hash(bytes32[] memory buffer) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := keccak256(add(buffer, 0x20), shl(5, mload(buffer)))
}
}
/// @dev Sets `buffer[i]` to `value`, without a bounds check.
/// Returns the `buffer` for function chaining.
function set(bytes32[] memory buffer, uint256 i, bytes32 value)
internal
pure
returns (bytes32[] memory)
{
/// @solidity memory-safe-assembly
assembly {
mstore(add(buffer, shl(5, add(1, i))), value)
}
return buffer;
}
/// @dev Sets `buffer[i]` to `value`, without a bounds check.
/// Returns the `buffer` for function chaining.
function set(bytes32[] memory buffer, uint256 i, uint256 value)
internal
pure
returns (bytes32[] memory)
{
/// @solidity memory-safe-assembly
assembly {
mstore(add(buffer, shl(5, add(1, i))), value)
}
return buffer;
}
/// @dev Returns `new bytes32[](n)`, without zeroing out the memory.
function malloc(uint256 n) internal pure returns (bytes32[] memory buffer) {
/// @solidity memory-safe-assembly
assembly {
buffer := mload(0x40)
mstore(buffer, n)
mstore(0x40, add(shl(5, add(1, n)), buffer))
}
}
/// @dev Frees memory that has been allocated for `buffer`.
/// No-op if `buffer.length` is zero, or if new memory has been allocated after `buffer`.
function free(bytes32[] memory buffer) internal pure {
/// @solidity memory-safe-assembly
assembly {
let n := mload(buffer)
mstore(shl(6, lt(iszero(n), eq(add(shl(5, add(1, n)), buffer), mload(0x40)))), buffer)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EQUALITY CHECKS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `a == abi.decode(b, (bytes32))`.
function eq(bytes32 a, bytes memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := and(eq(0x20, mload(b)), eq(a, mload(add(b, 0x20))))
}
}
/// @dev Returns `abi.decode(a, (bytes32)) == b`.
function eq(bytes memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := and(eq(0x20, mload(a)), eq(b, mload(add(a, 0x20))))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE SLICE HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function hash(bytes memory b, uint256 start, uint256 end)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
end := xor(end, mul(xor(end, n), lt(n, end)))
start := xor(start, mul(xor(start, n), lt(n, start)))
result := keccak256(add(add(b, 0x20), start), mul(gt(end, start), sub(end, start)))
}
}
/// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
function hash(bytes memory b, uint256 start) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
start := xor(start, mul(xor(start, n), lt(n, start)))
result := keccak256(add(add(b, 0x20), start), mul(gt(n, start), sub(n, start)))
}
}
/// @dev Returns the keccak256 of the bytes.
function hash(bytes memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := keccak256(add(b, 0x20), mload(b))
}
}
/// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function hashCalldata(bytes calldata b, uint256 start, uint256 end)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(end, start), sub(end, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := keccak256(mload(0x40), n)
}
}
/// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
function hashCalldata(bytes calldata b, uint256 start) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(b.length, start), sub(b.length, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := keccak256(mload(0x40), n)
}
}
/// @dev Returns the keccak256 of the bytes.
function hashCalldata(bytes calldata b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
calldatacopy(mload(0x40), b.offset, b.length)
result := keccak256(mload(0x40), b.length)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SHA2-256 HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `sha256(abi.encode(b))`. Yes, it's more efficient.
function sha2(bytes32 b) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, b)
result := mload(staticcall(gas(), 2, 0x00, 0x20, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function sha2(bytes memory b, uint256 start, uint256 end)
internal
view
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
end := xor(end, mul(xor(end, n), lt(n, end)))
start := xor(start, mul(xor(start, n), lt(n, start)))
// forgefmt: disable-next-item
result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
mul(gt(end, start), sub(end, start)), 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
function sha2(bytes memory b, uint256 start) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(b)
start := xor(start, mul(xor(start, n), lt(n, start)))
// forgefmt: disable-next-item
result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
mul(gt(n, start), sub(n, start)), 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the bytes.
function sha2(bytes memory b) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(staticcall(gas(), 2, add(b, 0x20), mload(b), 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function sha2Calldata(bytes calldata b, uint256 start, uint256 end)
internal
view
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(end, start), sub(end, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
function sha2Calldata(bytes calldata b, uint256 start) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
let n := mul(gt(b.length, start), sub(b.length, start))
calldatacopy(mload(0x40), add(b.offset, start), n)
result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
/// @dev Returns the sha256 of the bytes.
function sha2Calldata(bytes calldata b) internal view returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
calldatacopy(mload(0x40), b.offset, b.length)
result := mload(staticcall(gas(), 2, mload(0x40), b.length, 0x01, 0x20))
if iszero(returndatasize()) { invalid() }
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BIT TWIDDLING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Find last set.
/// Returns the index of the most significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
function fls(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Count leading zeros.
/// Returns the number of zeros preceding the most significant one bit.
/// If `x` is zero, returns 256.
function clz(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))
}
}
/// @dev Find first set.
/// Returns the index of the least significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
/// Equivalent to `ctz` (count trailing zeros), which gives
/// the number of zeros following the least significant one bit.
function ffs(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Isolate the least significant bit.
x := and(x, add(not(x), 1))
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
// forgefmt: disable-next-item
r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
0x8040405543005266443200005020610674053026020000107506200176117077)))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/// @dev Returns the number of set bits in `x`.
function popCount(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let isMax := eq(x, max)
x := sub(x, and(shr(1, x), div(max, 3)))
x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
x := and(add(x, shr(4, x)), div(max, 17))
c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
}
}
/// @dev Returns the number of zero bytes in `x`.
/// To get the number of non-zero bytes, simply do `32 - countZeroBytes(x)`.
function countZeroBytes(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
c := byte(0, mul(shr(7, not(m)), shr(7, not(or(or(add(and(x, m), m), x), m)))))
}
}
/// @dev Returns the number of zero bytes in `s`.
/// To get the number of non-zero bytes, simply do `s.length - countZeroBytes(s)`.
function countZeroBytes(bytes memory s) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
function czb(x_) -> _c {
let _m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
_c := shr(7, not(or(or(add(and(x_, _m), _m), x_), _m)))
_c := byte(0, mul(shr(7, not(_m)), _c))
}
let n := mload(s)
let l := shl(5, shr(5, n))
s := add(s, 0x20)
for { let i } xor(i, l) { i := add(i, 0x20) } { c := add(czb(mload(add(s, i))), c) }
if lt(l, n) { c := add(czb(or(shr(shl(3, sub(n, l)), not(0)), mload(add(s, l)))), c) }
}
}
/// @dev Returns the number of zero bytes in `s`.
/// To get the number of non-zero bytes, simply do `s.length - countZeroBytes(s)`.
function countZeroBytesCalldata(bytes calldata s) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
function czb(x_) -> _c {
let _m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
_c := shr(7, not(or(or(add(and(x_, _m), _m), x_), _m)))
_c := byte(0, mul(shr(7, not(_m)), _c))
}
let l := shl(5, shr(5, s.length))
for { let i } xor(i, l) { i := add(i, 0x20) } {
c := add(czb(calldataload(add(s.offset, i))), c)
}
if lt(l, s.length) {
let m := shr(shl(3, sub(s.length, l)), not(0))
c := add(czb(or(m, calldataload(add(s.offset, l)))), c)
}
}
}
/// @dev Returns whether `x` is a power of 2.
function isPo2(uint256 x) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `x && !(x & (x - 1))`.
result := iszero(add(and(x, sub(x, 1)), iszero(x)))
}
}
/// @dev Returns `x` reversed at the bit level.
function reverseBits(uint256 x) internal pure returns (uint256 r) {
uint256 m0 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 m1 = m0 ^ (m0 << 2);
uint256 m2 = m1 ^ (m1 << 1);
r = reverseBytes(x);
r = (m2 & (r >> 1)) | ((m2 & r) << 1);
r = (m1 & (r >> 2)) | ((m1 & r) << 2);
r = (m0 & (r >> 4)) | ((m0 & r) << 4);
}
/// @dev Returns `x` reversed at the byte level.
function reverseBytes(uint256 x) internal pure returns (uint256 r) {
unchecked {
// Computing masks on-the-fly reduces bytecode size by about 200 bytes.
uint256 m0 = 0x100000000000000000000000000000001 * (~toUint(x == uint256(0)) >> 192);
uint256 m1 = m0 ^ (m0 << 32);
uint256 m2 = m1 ^ (m1 << 16);
uint256 m3 = m2 ^ (m2 << 8);
r = (m3 & (x >> 8)) | ((m3 & x) << 8);
r = (m2 & (r >> 16)) | ((m2 & r) << 16);
r = (m1 & (r >> 32)) | ((m1 & r) << 32);
r = (m0 & (r >> 64)) | ((m0 & r) << 64);
r = (r >> 128) | (r << 128);
}
}
/// @dev Returns the common prefix of `x` and `y` at the bit level.
function commonBitPrefix(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
uint256 s = 256 - clz(x ^ y);
return (x >> s) << s;
}
}
/// @dev Returns the common prefix of `x` and `y` at the nibble level.
function commonNibblePrefix(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
uint256 s = (64 - (clz(x ^ y) >> 2)) << 2;
return (x >> s) << s;
}
}
/// @dev Returns the common prefix of `x` and `y` at the byte level.
function commonBytePrefix(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
uint256 s = (32 - (clz(x ^ y) >> 3)) << 3;
return (x >> s) << s;
}
}
/// @dev hex"ABCD" -> hex"0A0B0C0D".
function toNibbles(bytes memory s) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := mload(s)
mstore(result, add(n, n)) // Store the new length.
s := add(s, 0x20)
let o := add(result, 0x20)
// forgefmt: disable-next-item
for { let i := 0 } lt(i, n) { i := add(i, 0x10) } {
let x := shr(128, mload(add(s, i)))
x := and(0x0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff, or(shl(64, x), x))
x := and(0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff, or(shl(32, x), x))
x := and(0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff, or(shl(16, x), x))
x := and(0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff, or(shl(8, x), x))
mstore(add(o, add(i, i)),
and(0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f, or(shl(4, x), x)))
}
mstore(add(o, add(s, s)), 0) // Zeroize slot after result.
mstore(0x40, add(0x40, add(o, add(s, s)))) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BOOLEAN OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A Solidity bool on the stack or memory is represented as a 256-bit word.
// Non-zero values are true, zero is false.
// A clean bool is either 0 (false) or 1 (true) under the hood.
// Usually, if not always, the bool result of a regular Solidity expression,
// or the argument of a public/external function will be a clean bool.
// You can usually use the raw variants for more performance.
// If uncertain, test (best with exact compiler settings).
// Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).
/// @dev Returns `x & y`. Inputs must be clean.
function rawAnd(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(x, y)
}
}
/// @dev Returns `x & y`.
function and(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns `w & x & y`.
function and(bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(or(iszero(w), or(iszero(x), iszero(y))))
}
}
/// @dev Returns `v & w & x & y`.
function and(bool v, bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(or(or(iszero(v), iszero(w)), or(iszero(x), iszero(y))))
}
}
/// @dev Returns `x | y`. Inputs must be clean.
function rawOr(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, y)
}
}
/// @dev Returns `x | y`.
function or(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(or(x, y)))
}
}
/// @dev Returns `w | x | y`.
function or(bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(or(w, or(x, y))))
}
}
/// @dev Returns `v | w | x | y`.
function or(bool v, bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(or(v, or(w, or(x, y)))))
}
}
/// @dev Returns 1 if `b` is true, else 0. Input must be clean.
function rawToUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := b
}
}
/// @dev Returns 1 if `b` is true, else 0.
function toUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @notice Library for EIP7702 operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/LibEIP7702.sol)
library LibEIP7702 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Failed to deploy the EIP7702Proxy.
error DeploymentFailed();
/// @dev The proxy query has failed.
error ProxyQueryFailed();
/// @dev Failed to change the proxy admin.
error ChangeProxyAdminFailed();
/// @dev Failed to upgrade the proxy.
error UpgradeProxyFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ERC-1967 storage slot for the implementation in the proxy.
/// `uint256(keccak256("eip1967.proxy.implementation")) - 1`.
bytes32 internal constant ERC1967_IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @dev The transient storage slot for requesting the proxy to initialize the implementation.
/// `uint256(keccak256("eip7702.proxy.delegation.initialization.request")) - 1`.
/// While we would love to use a smaller constant, this slot is used in both the proxy
/// and the delegation, so we shall just use bytes32 in case we want to standardize this.
bytes32 internal constant EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT =
0x94e11c6e41e7fb92cb8bb65e13fdfbd4eba8b831292a1a220f7915c78c7c078f;
/// @dev The creation code for the EIP7702Proxy.
/// This is generated from `EIP7702Proxy.sol` with exact compilation settings.
bytes internal constant EIP7702_PROXY_CREATION_CODE =
hex"60c06040819052306080526102d63881900390819083398101604081905261002691610096565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8290557fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103819055811515110260a0526100c7565b80516001600160a01b0381168114610091575f5ffd5b919050565b5f5f604083850312156100a7575f5ffd5b6100b08361007b565b91506100be6020840161007b565b90509250929050565b60805160a0516101f06100e65f395f602701525f600601526101f05ff3fe60016040527f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc361960601c30841861010a576001361161008657815481165f5260205ff35b5f3560e01c80635c60da1b036100a157825482165f5260205ff35b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61038163f851a440036100d65780545f5260205ff35b80543303610106578382630900f01014028183638f2839701402178015610104576004358416815560206040f35b505b5f5ffd5b815481163660010361013b5780610133575082806101335760205f5f5f885afa15610106573d5ff35b805f5260205ff35b365f5f37806101a7575082806101a7576020365f5f885afa5f5f365f36515af416610168573d5f5f3e3d5ffd5b7f94e11c6e41e7fb92cb8bb65e13fdfbd4eba8b831292a1a220f7915c78c7c078f805c1561019e57365184548419161784555f815d5b503d5f5f3e3d5ff35b5f5f365f845af461019e573d5f5f3e3d5ffdfea2646970667358221220e8b1a2a38594baf32c154aa7dd7743c9cd741d4f386b5ab588a5dcd613c3a00e64736f6c634300081c0033";
/// @dev The keccak256 of runtime code for `EIP7702Proxy.sol` with exact compilation settings,
/// with immutables zeroized and without the CBOR metadata.
bytes32 internal constant EIP7702_PROXY_MINIMAL_CODE_HASH =
0xf8710866f390ac7c12640457f9cb9663657ac8168b7d4ce6418a982932b3043e;
/// @dev The length of the runtime code for `EIP7702Proxy.sol` with exact compilation settings,
/// with immutables zeroized and without the CBOR metadata.
uint256 internal constant EIP7702_PROXY_MINIMAL_CODE_LENGTH = 0x1ba;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* AUTHORITY AND PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the delegation of the account.
/// If the account is not an EIP7702 authority, returns `address(0)`.
function delegationOf(address account) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
extcodecopy(account, 0x00, 0x00, 0x20)
// Note: Checking that it starts with hex"ef01" is the most general and futureproof.
// 7702 bytecode is `abi.encodePacked(hex"ef01", uint8(version), address(delegation))`.
result := mul(shr(96, mload(0x03)), eq(0xef01, shr(240, mload(0x00))))
}
}
/// @dev Returns the delegation and the implementation of the account.
/// If the account delegation is not a valid EIP7702Proxy, returns `address(0)`.
function delegationAndImplementationOf(address account)
internal
view
returns (address delegation, address implementation)
{
delegation = delegationOf(account);
if (isEIP7702Proxy(delegation)) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0)
if iszero(staticcall(gas(), account, 0x00, 0x01, 0x00, 0x20)) { revert(0x00, 0x00) }
implementation := mload(0x00)
}
}
}
/// @dev Returns the implementation of `target`.
/// If `target` is neither an EIP7702Proxy nor an EOA delegated to an EIP7702Proxy, returns `address(0)`.
function implementationOf(address target) internal view returns (address result) {
if (!isEIP7702Proxy(target)) if (!isEIP7702Proxy(delegationOf(target))) return address(0);
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0)
if iszero(staticcall(gas(), target, 0x00, 0x01, 0x00, 0x20)) { revert(0x00, 0x00) }
result := mload(0x00)
}
}
/// @dev Returns if `target` is an valid EIP7702Proxy based on a bytecode hash check.
function isEIP7702Proxy(address target) internal view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
// Copy the runtime bytecode without the CBOR metadata.
extcodecopy(target, m, 0x00, EIP7702_PROXY_MINIMAL_CODE_LENGTH)
// Zeroize the immutables.
mstore(add(m, 0x06), 0) // The first `7f<immutable_word>`.
mstore(add(m, 0x27), 0) // The second `7f<immutable_word>`.
let h := keccak256(m, EIP7702_PROXY_MINIMAL_CODE_LENGTH)
result := eq(EIP7702_PROXY_MINIMAL_CODE_HASH, h)
}
}
/// @dev Returns the initialization code for the EIP7702Proxy.
function proxyInitCode(address initialImplementation, address initialAdmin)
internal
pure
returns (bytes memory)
{
return abi.encodePacked(
EIP7702_PROXY_CREATION_CODE,
uint256(uint160(initialImplementation)),
uint256(uint160(initialAdmin))
);
}
/// @dev Deploys an EIP7702Proxy.
function deployProxy(address initialImplementation, address initialAdmin)
internal
returns (address instance)
{
bytes memory initCode = proxyInitCode(initialImplementation, initialAdmin);
/// @solidity memory-safe-assembly
assembly {
instance := create(0, add(initCode, 0x20), mload(initCode))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys an EIP7702Proxy to a deterministic address with `salt`.
function deployProxyDeterministic(
address initialImplementation,
address initialAdmin,
bytes32 salt
) internal returns (address instance) {
bytes memory initCode = proxyInitCode(initialImplementation, initialAdmin);
/// @solidity memory-safe-assembly
assembly {
instance := create2(0, add(initCode, 0x20), mload(initCode), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns the admin of the proxy.
/// Assumes that the proxy is a proper EIP7702Proxy, if it exists.
function proxyAdmin(address proxy) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0xf851a440) // `admin()`.
let t := staticcall(gas(), proxy, 0x1c, 0x04, 0x00, 0x20)
if iszero(and(gt(returndatasize(), 0x1f), t)) {
mstore(0x00, 0x26ec9b6a) // `ProxyQueryFailed()`.
revert(0x1c, 0x04)
}
result := mload(0x00)
}
}
/// @dev Changes the admin on the proxy. The caller must be the admin.
/// Assumes that the proxy is a proper EIP7702Proxy, if it exists.
function changeProxyAdmin(address proxy, address newAdmin) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x8f283970) // `changeAdmin(address)`.
mstore(0x20, newAdmin) // The implementation will clean the upper 96 bits.
if iszero(and(eq(mload(0x00), 1), call(gas(), proxy, 0, 0x1c, 0x24, 0x00, 0x20))) {
mstore(0x00, 0xc502e37e) // `ChangeProxyAdminFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Changes the implementation on the proxy. The caller must be the admin.
/// Assumes that the proxy is a proper EIP7702Proxy, if it exists.
function upgradeProxy(address proxy, address newImplementation) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x0900f010) // `upgrade(address)`.
mstore(0x20, newImplementation) // The implementation will clean the upper 96 bits.
if iszero(and(eq(mload(0x00), 1), call(gas(), proxy, 0, 0x1c, 0x24, 0x00, 0x20))) {
mstore(0x00, 0xc6edd882) // `UpgradeProxyFailed()`.
revert(0x1c, 0x04)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UUPS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Upgrades the implementation.
/// The new implementation will NOT be active until the next UserOp or transaction.
/// To "auto-upgrade" to the latest implementation on the proxy, pass in `address(0)` to reset
/// the implementation slot. This causes the proxy to use the latest default implementation,
/// which may be optionally reinitialized via `requestProxyDelegationInitialization()`.
/// This function is intended to be used on the authority of an EIP7702Proxy delegation.
/// The most intended usage pattern is to wrap this in an access-gated admin function.
function upgradeProxyDelegation(address newImplementation) internal {
/// @solidity memory-safe-assembly
assembly {
let s := ERC1967_IMPLEMENTATION_SLOT
// Preserve the upper 96 bits when updating in case they are used for some stuff.
mstore(0x00, sload(s))
mstore(0x0c, shl(96, newImplementation))
sstore(s, mload(0x00))
}
}
/// @dev Requests the implementation to be initialized to the latest implementation on the proxy.
/// This function is intended to be used on the authority of an EIP7702Proxy delegation.
/// The most intended usage pattern is to place it at the end of an `execute` function.
function requestProxyDelegationInitialization() internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, sload(ERC1967_IMPLEMENTATION_SLOT))) {
// Use a dedicated transient storage slot for better Swiss-cheese-model safety.
tstore(EIP7702_PROXY_DELEGATION_INITIALIZATION_REQUEST_SLOT, address())
}
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] /// @title Execution Manager Events and Errors Interface /// @notice Interface for defining events and errors related to transaction execution processes within smart accounts. /// @dev This interface defines events and errors used by execution manager to handle and report the operational status /// of /// smart account transactions. /// It is a part of the Nexus suite of contracts aimed at implementing flexible and secure smart account operations. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IBaseAccountEventsAndErrors { /// @dev Throws an error when a caller is not authorized to access an account. error AccountAccessUnauthorized(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { SentinelListLib } from "sentinellist/SentinelList.sol"; import { IHook, IPreValidationHookERC1271, IPreValidationHookERC4337 } from "erc7579/interfaces/IERC7579Module.sol"; import { CallType } from "../../../lib/erc-7579/ModeLib.sol"; /// @title Nexus - IStorage Interface /// @notice Provides structured storage for Modular Smart Account under the Nexus suite, compliant with ERC-7579 and /// ERC-4337. /// @dev Manages structured storage using SentinelListLib for validators and executors, and a mapping for fallback /// handlers. /// This interface utilizes ERC-7201 storage location practices to ensure isolated and collision-resistant storage /// spaces /// within smart contracts. /// It is designed to support dynamic execution and modular management strategies essential for advanced smart account /// architectures. /// @custom:storage-location erc7201:biconomy.storage.Nexus /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IStorage { /// @notice Struct storing validators and executors using Sentinel lists, and fallback handlers via mapping. struct AccountStorage { ///< List of validators, initialized upon contract deployment. SentinelListLib.SentinelList validators; ///< List of executors, similarly initialized. SentinelListLib.SentinelList executors; ///< Mapping of selectors to their respective fallback handlers. mapping(bytes4 => FallbackHandler) fallbacks; ///< Current hook module associated with this account. IHook hook; ///< Mapping of hooks to requested timelocks. mapping(address hook => uint256) emergencyUninstallTimelock; ///< PreValidation hook for validateUserOp IPreValidationHookERC4337 preValidationHookERC4337; ///< PreValidation hook for isValidSignature IPreValidationHookERC1271 preValidationHookERC1271; ///< Mapping of used nonces for replay protection. mapping(uint256 => bool) nonces; ///< ERC-7484 registry address registry; // keeping this to avoid collisions b/w versions ///< Mapping of used 7702 init hashes for replay protection. mapping(bytes32 => bool) erc7702InitHashes; } /// @notice Defines a fallback handler with an associated handler address and a call type. struct FallbackHandler { ///< The address of the fallback function handler. address handler; ///< The type of call this handler supports (e.g., static or call). CallType calltype; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { CallType } from "../../../lib/erc-7579/ModeLib.sol"; /// @title ERC-7579 Module Manager Events and Errors Interface /// @notice Provides event and error definitions for actions related to module management in smart accounts. /// @dev Used by IModuleManager to define the events and errors associated with the installation and management of /// modules. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IModuleManagerEventsAndErrors { /// @notice Emitted when a module is installed onto a smart account. /// @param moduleTypeId The identifier for the type of module installed. /// @param module The address of the installed module. event ModuleInstalled(uint256 moduleTypeId, address module); /// @notice Emitted when a module is uninstalled from a smart account. /// @param moduleTypeId The identifier for the type of module uninstalled. /// @param module The address of the uninstalled module. event ModuleUninstalled(uint256 moduleTypeId, address module); /// @dev Thrown when the specified module address is not recognized as valid. error InvalidModule(address module); /// @dev Thrown when an invalid module type identifier is provided. error InvalidModuleTypeId(uint256 moduleTypeId); /// @dev Thrown when there is an attempt to install a module that is already installed. error ModuleAlreadyInstalled(uint256 moduleTypeId, address module); /// @dev Thrown when an operation is performed by an unauthorized operator. error UnauthorizedOperation(address operator); /// @dev Thrown when there is an attempt to uninstall a module that is not installed. error ModuleNotInstalled(uint256 moduleTypeId, address module); /// @dev Thrown when a module address is set to zero. error ModuleAddressCanNotBeZero(); /// @dev Thrown when a post-check fails after hook execution. error HookPostCheckFailed(); /// @dev Thrown when there is an attempt to install a hook while another is already installed. error HookAlreadyInstalled(address currentHook); /// @dev Thrown when there is an attempt to install a PreValidationHook while another is already installed. error PrevalidationHookAlreadyInstalled(address currentPreValidationHook); /// @dev Thrown when there is an attempt to install a fallback handler for a selector already having one. error FallbackAlreadyInstalledForSelector(bytes4 selector); /// @dev Thrown when there is an attempt to uninstall a fallback handler for a selector that does not have one /// installed. error FallbackNotInstalledForSelector(bytes4 selector); /// @dev Thrown when Invalid data is provided for MultiType install flow error InvalidInput(); /// @dev Thrown when unable to validate Emergency Uninstall signature error EmergencyUninstallSigError(); /// @notice Error thrown when an invalid nonce is used error InvalidNonce(); /// Error thrown when account installs/uninstalls module with mismatched moduleTypeId error MismatchModuleTypeId(); /// @dev Thrown when there is an attempt to install a forbidden selector as a fallback handler. error FallbackSelectorForbidden(); /// @dev Thrown when there is an attempt to install a fallback handler with an invalid calltype for a given /// selector. error FallbackCallTypeInvalid(); /// @notice Error thrown when an execution with an unsupported CallType was made. /// @param callType The unsupported call type. error UnsupportedCallType(CallType callType); /// @notice Error thrown when the default validator is already installed. error DefaultValidatorAlreadyInstalled(); }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/**
* @title ModeLib
* To allow smart accounts to be very simple, but allow for more complex execution, A custom mode
* encoding is used.
* Function Signature of execute function:
* function execute(ModeCode mode, bytes calldata executionCalldata) external payable;
* This allows for a single bytes32 to be used to encode the execution mode, calltype, execType and
* context.
* NOTE: Simple Account implementations only have to scope for the most significant byte. Account that
* implement
* more complex execution modes may use the entire bytes32.
*
* |--------------------------------------------------------------------|
* | CALLTYPE | EXECTYPE | UNUSED | ModeSelector | ModePayload |
* |--------------------------------------------------------------------|
* | 1 byte | 1 byte | 4 bytes | 4 bytes | 22 bytes |
* |--------------------------------------------------------------------|
*
* CALLTYPE: 1 byte
* CallType is used to determine how the executeCalldata paramter of the execute function has to be
* decoded.
* It can be either single, batch or delegatecall. In the future different calls could be added.
* CALLTYPE can be used by a validation module to determine how to decode <userOp.callData[36:]>.
*
* EXECTYPE: 1 byte
* ExecType is used to determine how the account should handle the execution.
* It can indicate if the execution should revert on failure or continue execution.
* In the future more execution modes may be added.
* Default Behavior (EXECTYPE = 0x00) is to revert on a single failed execution. If one execution in
* a batch fails, the entire batch is reverted
*
* UNUSED: 4 bytes
* Unused bytes are reserved for future use.
*
* ModeSelector: bytes4
* The "optional" mode selector can be used by account vendors, to implement custom behavior in
* their accounts.
* the way a ModeSelector is to be calculated is bytes4(keccak256("vendorname.featurename"))
* this is to prevent collisions between different vendors, while allowing innovation and the
* development of new features without coordination between ERC-7579 implementing accounts
*
* ModePayload: 22 bytes
* Mode payload is used to pass additional data to the smart account execution, this may be
* interpreted depending on the ModeSelector
*
* ExecutionCallData: n bytes
* single, delegatecall or batch exec abi.encoded as bytes
*/
import { Execution } from "../interfaces/IERC7579Account.sol";
// Custom type for improved developer experience
type ModeCode is bytes32;
type CallType is bytes1;
type ExecType is bytes1;
type ModeSelector is bytes4;
type ModePayload is bytes22;
// Default CallType
CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
// Batched CallType
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
// @dev Implementing delegatecall is OPTIONAL!
// implement delegatecall with extreme care.
CallType constant CALLTYPE_STATIC = CallType.wrap(0xFE);
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
// @dev default behavior is to revert on failure
// To allow very simple accounts to use mode encoding, the default behavior is to revert on failure
// Since this is value 0x00, no additional encoding is required for simple accounts
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
// @dev account may elect to change execution behavior. For example "try exec" / "allow fail"
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
ModeSelector constant MODE_DEFAULT = ModeSelector.wrap(bytes4(0x00000000));
// Example declaration of a custom mode selector
ModeSelector constant MODE_OFFSET = ModeSelector.wrap(bytes4(keccak256("default.mode.offset")));
/**
* @dev ModeLib is a helper library to encode/decode ModeCodes
*/
library ModeLib {
function decode(ModeCode mode)
internal
pure
returns (
CallType _calltype,
ExecType _execType,
ModeSelector _modeSelector,
ModePayload _modePayload
)
{
assembly {
_calltype := mode
_execType := shl(8, mode)
_modeSelector := shl(48, mode)
_modePayload := shl(80, mode)
}
}
function encode(
CallType callType,
ExecType execType,
ModeSelector mode,
ModePayload payload
)
internal
pure
returns (ModeCode)
{
return ModeCode.wrap(
bytes32(
abi.encodePacked(callType, execType, bytes4(0), ModeSelector.unwrap(mode), payload)
)
);
}
function encodeSimpleBatch() internal pure returns (ModeCode mode) {
mode = encode(CALLTYPE_BATCH, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeSimpleSingle() internal pure returns (ModeCode mode) {
mode = encode(CALLTYPE_SINGLE, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function getCallType(ModeCode mode) internal pure returns (CallType calltype) {
assembly {
calltype := mode
}
}
}
using { eqModeSelector as == } for ModeSelector global;
using { eqCallType as == } for CallType global;
using { eqExecType as == } for ExecType global;
function eqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) == CallType.unwrap(b);
}
function eqExecType(ExecType a, ExecType b) pure returns (bool) {
return ExecType.unwrap(a) == ExecType.unwrap(b);
}
function eqModeSelector(ModeSelector a, ModeSelector b) pure returns (bool) {
return ModeSelector.unwrap(a) == ModeSelector.unwrap(b);
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] /// @title Execution Manager Events and Errors Interface /// @notice Interface for defining events and errors related to transaction execution processes within smart accounts. /// @dev This interface defines events and errors used by execution manager to handle and report the operational status /// of /// smart account transactions. /// It is a part of the Nexus suite of contracts aimed at implementing flexible and secure smart account operations. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady import { ExecType } from "../../../lib/erc-7579/ModeLib.sol"; interface IExecutionHelperEventsAndErrors { /// @notice Event emitted when a transaction fails to execute successfully. event TryExecuteUnsuccessful(bytes callData, bytes result); /// @notice Event emitted when a transaction fails to execute successfully. event TryDelegateCallUnsuccessful(bytes callData, bytes result); /// @notice Error thrown when an execution with an unsupported ExecType was made. /// @param execType The unsupported execution type. error UnsupportedExecType(ExecType execType); }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { EfficientHashLib } from "solady/utils/EfficientHashLib.sol";
/**
* @title ComposableStorage
* @dev Contract to handle generic storage operations with cross-chain support
*/
contract ComposableStorage {
using EfficientHashLib for *;
error SlotNotInitialized();
// Mapping to track initialized slots
mapping(bytes32 => bool) private initializedSlots;
// Mapping to track length of dynamic data
mapping(bytes32 => uint256) private dynamicDataLength;
/**
* @dev Internal function to write a value to a specific storage slot
*/
function _writeStorage(bytes32 slot, bytes32 value, bytes32 namespace) private {
bytes32 namespacedSlot = getNamespacedSlot(namespace, slot);
initializedSlots[namespacedSlot] = true;
assembly {
sstore(namespacedSlot, value)
}
}
/**
* @dev Write a value to a specific storage slot
* @param slot The storage slot to write to
* @param value The value to write
*/
function writeStorage(bytes32 slot, bytes32 value, address account) external {
bytes32 namespace = getNamespace(account, msg.sender);
_writeStorage(slot, value, namespace);
}
/**
* @dev Read a value from a specific namespace and slot
* @param namespace The namespace (typically a contract address)
* @param slot The storage slot to read from
* @return The value stored at the specified namespaced slot
*/
function readStorage(bytes32 namespace, bytes32 slot) external view returns (bytes32) {
bytes32 namespacedSlot = getNamespacedSlot(namespace, slot);
if (!initializedSlots[namespacedSlot]) {
revert SlotNotInitialized();
}
bytes32 value;
assembly {
value := sload(namespacedSlot)
}
return value;
}
/**
* @dev Generates a namespaced slot
* @param namespace The namespace (typically a contract address)
* @param slot The storage slot to read from
* return The namespaced slot
*/
function getNamespacedSlot(bytes32 namespace, bytes32 slot) public pure returns (bytes32 result) {
assembly {
mstore(0x00, namespace)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Generates a namespace for a given account and caller
* @param account The account address
* @param _caller The caller address
* return The generated namespace
*/
function getNamespace(address account, address _caller) public pure returns (bytes32 result) {
assembly {
mstore(0x00, account)
mstore(0x14, _caller)
result := keccak256(0x0c, 0x28)
}
}
/**
* @dev Check if a slot has been initialized
*/
function isSlotInitialized(bytes32 namespace, bytes32 slot) external view returns (bool) {
bytes32 namespacedSlot = getNamespacedSlot(namespace, slot);
return initializedSlots[namespacedSlot];
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}{
"remappings": [
"forge-std/=node_modules/forge-std/src/",
"account-abstraction/=node_modules/account-abstraction/contracts/",
"solady/=node_modules/solady/src/",
"sentinellist/=node_modules/@rhinestone/sentinellist/src/",
"module-bases/=node_modules/@rhinestone/module-bases/src/",
"erc7739Validator/=node_modules/@erc7579/erc7739-validator-base/src/",
"EnumerableSet4337/=node_modules/@erc7579/enumerablemap4337/src/",
"erc7579/=node_modules/@erc7579/implementation/src/",
"byteslib/=node_modules/solidity-bytes-utils/contracts/",
"rlp-reader/=node_modules/solidity-rlp/contracts/",
"murky-trees/=node_modules/murky/src/",
"solarray/=node_modules/solarray/src/",
"excessively-safe-call/=node_modules/excessively-safe-call/src/",
"@ERC4337/=node_modules/@ERC4337/",
"@erc7579/=node_modules/@erc7579/",
"@gnosis.pm/=node_modules/@gnosis.pm/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@prb/=node_modules/@prb/",
"@rhinestone/=node_modules/@rhinestone/",
"@safe-global/=node_modules/@safe-global/",
"@zerodev/=node_modules/@zerodev/",
"account-abstraction-v0.6/=node_modules/account-abstraction-v0.6/",
"ds-test/=node_modules/ds-test/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"solidity-bytes-utils/=node_modules/solidity-bytes-utils/",
"solidity-rlp/=node_modules/solidity-rlp/"
],
"optimizer": {
"enabled": true,
"runs": 999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"anEntryPoint","type":"address"},{"internalType":"address","name":"defaultValidator","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountAccessUnauthorized","type":"error"},{"inputs":[],"name":"AccountAlreadyInitialized","type":"error"},{"inputs":[],"name":"AccountNotInitialized","type":"error"},{"inputs":[{"internalType":"enum ConstraintType","name":"constraintType","type":"uint8"}],"name":"ConstraintNotMet","type":"error"},{"inputs":[],"name":"DefaultValidatorAlreadyInstalled","type":"error"},{"inputs":[],"name":"ERC7702AccountCannotBeUpgradedThisWay","type":"error"},{"inputs":[],"name":"EmergencyTimeLockNotExpired","type":"error"},{"inputs":[],"name":"EmergencyUninstallSigError","type":"error"},{"inputs":[],"name":"EntryPointCanNotBeZero","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"FallbackAlreadyInstalledForSelector","type":"error"},{"inputs":[],"name":"FallbackCallTypeInvalid","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"FallbackNotInstalledForSelector","type":"error"},{"inputs":[],"name":"FallbackSelectorForbidden","type":"error"},{"inputs":[{"internalType":"address","name":"currentHook","type":"address"}],"name":"HookAlreadyInstalled","type":"error"},{"inputs":[],"name":"HookPostCheckFailed","type":"error"},{"inputs":[],"name":"InvalidConstraintType","type":"error"},{"inputs":[],"name":"InvalidImplementationAddress","type":"error"},{"inputs":[],"name":"InvalidInitData","type":"error"},{"inputs":[],"name":"InvalidInput","type":"error"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"InvalidModule","type":"error"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"}],"name":"InvalidModuleTypeId","type":"error"},{"inputs":[],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidOutputParamFetcherType","type":"error"},{"inputs":[{"internalType":"string","name":"message","type":"string"}],"name":"InvalidParameterEncoding","type":"error"},{"inputs":[{"internalType":"string","name":"message","type":"string"}],"name":"InvalidSetOfInputParams","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"LinkedList_AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"entry","type":"address"}],"name":"LinkedList_EntryAlreadyInList","type":"error"},{"inputs":[{"internalType":"address","name":"entry","type":"address"}],"name":"LinkedList_InvalidEntry","type":"error"},{"inputs":[],"name":"LinkedList_InvalidPage","type":"error"},{"inputs":[],"name":"MismatchModuleTypeId","type":"error"},{"inputs":[],"name":"ModuleAddressCanNotBeZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"internalType":"address","name":"module","type":"address"}],"name":"ModuleAlreadyInstalled","type":"error"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"internalType":"address","name":"module","type":"address"}],"name":"ModuleNotInstalled","type":"error"},{"inputs":[],"name":"Output_StaticCallFailed","type":"error"},{"inputs":[{"internalType":"address","name":"currentPreValidationHook","type":"address"}],"name":"PrevalidationHookAlreadyInstalled","type":"error"},{"inputs":[],"name":"UnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"UnauthorizedOperation","type":"error"},{"inputs":[{"internalType":"CallType","name":"callType","type":"bytes1"}],"name":"UnsupportedCallType","type":"error"},{"inputs":[{"internalType":"ExecType","name":"execType","type":"bytes1"}],"name":"UnsupportedExecType","type":"error"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"}],"name":"UnsupportedModuleType","type":"error"},{"inputs":[],"name":"UpgradeFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hook","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"EmergencyHookUninstallRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hook","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"EmergencyHookUninstallRequestReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"ModuleInstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"ModuleUninstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"callData","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"result","type":"bytes"}],"name":"TryDelegateCallUnsuccessful","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"callData","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"result","type":"bytes"}],"name":"TryExecuteUnsuccessful","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"accountId","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"addDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"hook","type":"address"},{"internalType":"uint256","name":"hookType","type":"uint256"},{"internalType":"bytes","name":"deInitData","type":"bytes"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"internalType":"struct EmergencyUninstall","name":"data","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"emergencyUninstallHook","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ExecutionMode","name":"mode","type":"bytes32"},{"internalType":"bytes","name":"executionCalldata","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes4","name":"functionSig","type":"bytes4"},{"components":[{"internalType":"enum InputParamType","name":"paramType","type":"uint8"},{"internalType":"enum InputParamFetcherType","name":"fetcherType","type":"uint8"},{"internalType":"bytes","name":"paramData","type":"bytes"},{"components":[{"internalType":"enum ConstraintType","name":"constraintType","type":"uint8"},{"internalType":"bytes","name":"referenceData","type":"bytes"}],"internalType":"struct Constraint[]","name":"constraints","type":"tuple[]"}],"internalType":"struct InputParam[]","name":"inputParams","type":"tuple[]"},{"components":[{"internalType":"enum OutputParamFetcherType","name":"fetcherType","type":"uint8"},{"internalType":"bytes","name":"paramData","type":"bytes"}],"internalType":"struct OutputParam[]","name":"outputParams","type":"tuple[]"}],"internalType":"struct ComposableExecution[]","name":"executions","type":"tuple[]"}],"name":"executeComposable","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"ExecutionMode","name":"mode","type":"bytes32"},{"internalType":"bytes","name":"executionCalldata","type":"bytes"}],"name":"executeFromExecutor","outputs":[{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"payable","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":"","type":"bytes32"}],"name":"executeUserOp","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getActiveHook","outputs":[{"internalType":"address","name":"hook","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDefaultValidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDeposit","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"cursor","type":"address"},{"internalType":"uint256","name":"size","type":"uint256"}],"name":"getExecutorsPaginated","outputs":[{"internalType":"address[]","name":"array","type":"address[]"},{"internalType":"address","name":"next","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"getFallbackHandlerBySelector","outputs":[{"internalType":"CallType","name":"","type":"bytes1"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getImplementation","outputs":[{"internalType":"address","name":"implementation","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"cursor","type":"address"},{"internalType":"uint256","name":"size","type":"uint256"}],"name":"getValidatorsPaginated","outputs":[{"internalType":"address[]","name":"array","type":"address[]"},{"internalType":"address","name":"next","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"initData","type":"bytes"}],"name":"initializeAccount","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"name":"installModule","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"additionalContext","type":"bytes"}],"name":"isModuleInstalled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ExecutionMode","name":"mode","type":"bytes32"}],"name":"supportsExecutionMode","outputs":[{"internalType":"bool","name":"isSupported","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"}],"name":"supportsModule","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"deInitData","type":"bytes"}],"name":"uninstallModule","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","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":"op","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"},{"internalType":"uint256","name":"missingAccountFunds","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"validationData","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawDepositTo","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
610180806040523461030a5761637d803803809161001d8285610376565b833981019060608183031261030a5761003581610399565b9061004260208201610399565b604082015190916001600160401b03821161030a57019280601f8501121561030a5783516001600160401b038111610362576040519461008c601f8301601f191660200187610376565b81865260208601926020838301011161030a57815f926020809301855e860101523060a0524660c05260409360a085516100c68782610376565b600581526020810190644e6578757360d81b82528751916100e78984610376565b6005835264312e332e3160d81b6020938401529051902060e08190527fd6f9477f1bbaaf55b72b2869a6c4697af6529f6af9f67d87058f1bf5bec0188761010081905288517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815280840192909252818901524660608201523060808201529190912061012052855163ecd0596160e01b8152600160048201526001600160a01b038516939181602481875afa908115610358575f9161031d575b501561030e57823b1561030a576044925f928388519586809581946306d61fe760e41b8352602060048401525180918160248501528484015e8181018301849052601f01601f191681010301925af18015610300576102f0575b506101405230610160526001600160a01b038116156102e15760805251615fcf90816103ae82396080518181816103c90152818161068f0152818161082301528181610f5e0152818161113401528181611206015281816113db0152818161152d015281816115760152818161184501526121a2015260a051816144d2015260c051816144f5015260e051816145650152610100518161458b015261012051816144b101526101405181818161024101528181611fb101528181612b9101528181612bee01528181613dc001526153420152610160518181816109de01526121710152f35b6307e355bf60e31b5f5260045ffd5b5f6102fa91610376565b5f6101fc565b84513d5f823e3d90fd5b5f80fd5b631c4f83bb60e31b5f5260045ffd5b90506020813d602011610350575b8161033860209383610376565b8101031261030a5751801515810361030a575f6101a2565b3d915061032b565b87513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761036257604052565b51906001600160a01b038216820361030a5756fe60806040526004361015610015575b366127fd57005b5f3560e01c80630a664dba146101d45780630b3dc354146101cf578063112d3a7d146101ca5780631626ba7e146101c557806319822f7c146101c0578063392e53cd146101bb578063481ddd23146101b65780634a58db19146101b15780634b6a1419146101ac5780634d44560d146101a75780634f1ef286146101a257806352d1902d1461019d5780635bfeadbb146101985780635faac46b146101935780637eba07b81461018e57806384b0196e146101895780638dd7712f146101845780639517e29f1461017f5780639cfd7cff1461017a578063a71763a814610175578063aaf10f4214610170578063b0d691fe1461016b578063c399ec8814610166578063d03c791414610161578063d691c9641461015c578063e9ae5c5314610157578063ea5f61d0146101525763f2dc691d0361000e57611a00565b611904565b61182e565b6116e7565b6115a7565b611551565b61150e565b6114b0565b6113c7565b611368565b6111ef565b6110fb565b611041565b610f00565b610d41565b610a39565b6109cb565b6108a3565b610808565b6106b7565b61067d565b6105e8565b6105bc565b610378565b610317565b6102f1565b610222565b6101e7565b5f9103126101e357565b5f80fd5b346101e3575f3660031901126101e35760206001600160a01b035f516020615f835f395f51905f5254166001600160a01b0360405191168152f35b346101e3575f3660031901126101e35760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b6001600160a01b038116036101e357565b359061028182610265565b565b9181601f840112156101e35782359167ffffffffffffffff83116101e357602083818601950101116101e357565b60606003198201126101e357600435916024356102cd81610265565b916044359067ffffffffffffffff82116101e3576102ed91600401610283565b9091565b346101e357602061030d610304366102b1565b92919091612a56565b6040519015158152f35b346101e35760403660031901126101e35760043560243567ffffffffffffffff81116101e357602091610351610357923690600401610283565b91611bd5565b6001600160e01b031960405191168152f35b90816101209103126101e35790565b346101e35760603660031901126101e35760043567ffffffffffffffff81116101e3576103ac61047a913690600401610369565b602061042960243561043460443580956103f06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163314611cac565b61042e6103fd3683611d76565b95869288810135906104138260031a60f81b1590565b156104f3575b5060401c6001600160a01b031690565b612bdd565b9361301b565b61010084929401525f6001600160a01b036040518098819682957f9700320300000000000000000000000000000000000000000000000000000000845260048401611f5a565b0393165af19081156104ee576104a9925f926104bd575b50806104ad575b506040519081529081906020820190565b0390f35b5f9081803892335af1505f610498565b8192506104e19060203d6020116104e7575b6104d98183611b6b565b810190611eb8565b91610491565b503d6104cf565b611bca565b600382901a60f81b600160f81b036105355761052161051a82610100610528940190611e85565b908a612f27565b3691611d25565b6101008501525b5f610419565b600382901a60f81b7f02000000000000000000000000000000000000000000000000000000000000001461056a575b5061052f565b6105ab61059f6105996105b69361058e610589610585611f8e565b1590565b611e56565b610100810190611e85565b90612d39565b93919290923691611d25565b610100880152612e50565b5f610564565b346101e3575f3660031901126101e357602061030d611f8e565b6001600160e01b03198116036101e357565b346101e35760203660031901126101e3576040602061064360043561060c816105d6565b6001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b82519061064f82611b4a565b54906001600160f81b03196001600160a01b0383169283835260581b16928391015282519182526020820152f35b5f3660031901126101e3575f388180347f00000000000000000000000000000000000000000000000000000000000000005af1156101e357005b60203660031901126101e35760043567ffffffffffffffff81116101e3576106e66106f8913690600401610283565b9081813033036106fa575b5050612e50565b005b91610706939193613154565b156107f75750506107f06107e38261072d85610725816107b697611a58565b979093611a66565b9490956107b06107a461079e8998809a816040519182372095610754610589610585611f8e565b61079561058961058561078e8a5f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0960205260405f2090565b5460ff1690565b30943691611d25565b8561319f565b6001600160a01b031690565b14612023565b5f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0960205260405f2090565b805460ff19166001179055565b5f806106f1565b91509150610803613176565b6107f0565b60403660031901126101e35760043561082081610265565b5f7f0000000000000000000000000000000000000000000000000000000000000000602435336001600160a01b03831614801561089a575b61086190611cac565b604051936014526034526f205c28780000000000000000000000008252604460108338935af115610892575f603452005b3d5f823e3d90fd5b50333014610858565b60403660031901126101e3576004356108bb81610265565b60243567ffffffffffffffff81116101e3576108db903690600401610283565b906001600160a01b036109026001600160a01b035f516020615f835f395f51905f52541690565b168061091257506106f892612144565b90916040519263d68f602560e01b84525f8480610934363433600485016120de565b038183875af19384156104ee575f946109a3575b50610954929394612144565b803b156101e35761097f5f92918392604051948580948193630b9dfbed60e11b835260048301612104565b03925af180156104ee5761098f57005b8061099d5f6106f893611b6b565b806101d9565b6109549394506109c4903d805f833e6109bc8183611b6b565b810190612098565b9392610948565b346101e3575f3660031901126101e357307f000000000000000000000000000000000000000000000000000000000000000003610a2c5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b639f03a0265f526004601cfd5b60403660031901126101e35760043567ffffffffffffffff81116101e3578060040190608060031982360301126101e35760243567ffffffffffffffff81116101e357610a8d610a94913690600401610283565b908461329e565b610ab06024820135926044610aa8826122c4565b930190611e85565b929060048214828115610cdb575b610ac7916122ce565b610add8383610ad887858484612a56565b612301565b610b17836001600160a01b03165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0460205260405f2090565b5480610ba8575050507f2841d18703faaff388732165e48fe431468531b1b1e626b1b7cbcbfc0d79c7409150610ba39042610b82826001600160a01b03165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0460205260405f2090565b55604080516001600160a01b03909216825242602083015290918291820190565b0390a1005b610bb56203f48082612353565b4210610c20575050507fcbd44a75f6935b5837022648b6c8487db984701200c5381c7c0f8c2b1d69b9da9150610ba39042610b82826001600160a01b03165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0460205260405f2090565b610c2990612343565b4210610cb3577f341347516a9de374859dfda710fa4828b2d48cb57d4fbe4c1149612b8e02276e93610c98915f610c90866001600160a01b03165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0460205260405f2090565b558385613461565b604080519182526001600160a01b03929092166020820152a1005b7f07f2f2d2000000000000000000000000000000000000000000000000000000005f5260045ffd5b60026007198201109150610abe565b90929192604082016040835281518091526020606084019201905f5b818110610d22575050506001600160a01b036020919416910152565b82516001600160a01b0316845260209384019390920191600101610d06565b346101e35760403660031901126101e357600435610d5e81610265565b6024359060016001600160a01b038216141580610eee575b610ed3578115610eab5790610d8a8161465a565b610dc2610db55f946001600160a01b03165f525f516020615f635f395f51905f5260205260405f2090565b546001600160a01b031690565b6001600160a01b0381168015159081610e9f575b5080610e96575b15610e3757610e2b610db582610e08610e3194610dfa8988613f46565b906001600160a01b03169052565b6001600160a01b03165f525f516020615f635f395f51905f5260205260405f2090565b9361468c565b92610dc2565b908360016001600160a01b038416141580610e8d575b610e63575b81526104a960405192839283610cea565b9150610e87610e7a610e748461469a565b83613f46565b516001600160a01b031690565b91610e52565b50801515610e4d565b50828410610ddd565b6001915014155f610dd6565b7ff7250817000000000000000000000000000000000000000000000000000000005f5260045ffd5b637c84ecfb60e01b5f526001600160a01b031660045260245ffd5b50610efb61058582613e1e565b610d76565b60203660031901126101e35760043567ffffffffffffffff81116101e357366023820112156101e357806004013567ffffffffffffffff81116101e3576024820191602436918360051b0101116101e357610f856001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163314611cac565b6001600160a01b03610fab6001600160a01b035f516020615f835f395f51905f52541690565b1680610fbb57506106f8916135e1565b906040519263d68f602560e01b84525f8480610fdc363433600485016120de565b038183875af19384156104ee575f94610ffb575b5090610954916135e1565b61095492919450611015903d805f833e6109bc8183611b6b565b939091610ff0565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346101e3575f3660031901126101e35761109c6110aa61105f613981565b6040929192519384937f0f00000000000000000000000000000000000000000000000000000000000000855260e0602086015260e085019061101d565b90838203604085015261101d565b4660608301523060808301525f60a083015281810360c083015260206060519182815201906080905f5b8181106110e2575050500390f35b82518452859450602093840193909201916001016110d4565b60403660031901126101e35760043567ffffffffffffffff81116101e357611127903690600401610369565b61115b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163314611cac565b6001600160a01b036111816001600160a01b035f516020615f835f395f51905f52541690565b168061119157506106f89061239c565b6040519163d68f602560e01b83525f83806111b1363433600485016120de565b038183865af19283156104ee575f936111cf575b506109549061239c565b6109549193506111e8903d805f833e6109bc8183611b6b565b92906111c5565b6111f8366102b1565b906001600160a01b039392937f0000000000000000000000000000000000000000000000000000000000000000163314801561135f575b61123890611cac565b6112436105856130c4565b611352575b6001600160a01b0384161561132a5760018303611296579061126a9184615260565b5f526020527fd21d0b289f126c4b473ea641963e766833c2f13866e4ff480abd787c100ef12360405fa1005b600283036112ae57906112a99184615160565b61126a565b600383036112c157906112a99184614e7c565b600483036112d457906112a99184614d0f565b6002600719840110156112ed57906112a9918484614b25565b826112fd57906112a991846149fc565b7f098312d2000000000000000000000000000000000000000000000000000000005f52600483905260245ffd5b7f5316c18d000000000000000000000000000000000000000000000000000000005f5260045ffd5b61135a6148c7565b611248565b5033301461122f565b346101e3575f3660031901126101e3576104a9604051611389604082611b6b565b601481527f6269636f6e6f6d792e6e657875732e312e332e31000000000000000000000000602082015260405191829160208352602083019061101d565b6113d0366102b1565b916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016331480156114a7575b61140d90611cac565b6001600160a01b036114336001600160a01b035f516020615f835f395f51905f52541690565b168061144357506106f8936123f6565b916040939193519363d68f602560e01b85525f8580611467363433600485016120de565b038183885af19485156104ee575f95611487575b506109549394956123f6565b6109549495506114a0903d805f833e6109bc8183611b6b565b949361147b565b50333014611404565b346101e3575f3660031901126101e3577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b03811615611506575b6020906001600160a01b0360405191168152f35b5030546114f2565b346101e3575f3660031901126101e35760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101e3575f3660031901126101e357306020526370a082315f52602080806024601c7f00000000000000000000000000000000000000000000000000000000000000005afa601f3d1116815102604051908152f35b346101e35760203660031901126101e3576104a96004358060081b6001600160f81b0319821615918215611640575b821561162b575b50816115f7575b5060405190151581529081906020820190565b6001600160f81b031981161591508115611613575b505f6115e4565b6001600160f81b031916600160f81b1490505f61160c565b6001600160f81b03199081161491505f6115dd565b6001600160f81b03198116600160f81b1492506115d6565b9060406003198301126101e357600435916024359067ffffffffffffffff82116101e3576102ed91600401610283565b602081016020825282518091526040820191602060408360051b8301019401925f915b8383106116ba57505050505090565b90919293946020806116d8600193603f19868203018752895161101d565b970193019301919392906116ab565b6116f036611658565b916116fa33613e5e565b15611802576001600160a01b036117256001600160a01b035f516020615f835f395f51905f52541690565b168061174657509161173a916104a993612598565b60405191829182611688565b90926040519263d68f602560e01b84525f8480611768363433600485016120de565b038183875af19384156104ee575f946117e2575b50611788929394612598565b90803b156101e3576117b45f93918492604051958680948193630b9dfbed60e11b835260048301612104565b03925af19182156104ee576104a9926117ce575b5061173a565b8061099d5f6117dc93611b6b565b5f6117c8565b6117889394506117fb903d805f833e6109bc8183611b6b565b939261177c565b7fb927fe5e000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b61183736611658565b9061186c6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163314611cac565b6001600160a01b036118926001600160a01b035f516020615f835f395f51905f52541690565b16806118a257506106f8926126ea565b90916040519263d68f602560e01b84525f84806118c4363433600485016120de565b038183875af19384156104ee575f946118e4575b506109549293946126ea565b6109549394506118fd903d805f833e6109bc8183611b6b565b93926118d8565b346101e35760403660031901126101e35760043561192181610265565b6024359060016001600160a01b0382161415806119ee575b610ed3578115610eab579061194d8161465a565b611978610db55f946001600160a01b03165f525f516020615fa35f395f51905f5260205260405f2090565b6001600160a01b03811680151590816119e2575b50806119d9575b15610e3757610e2b610db5826119b06119d394610dfa8988613f46565b6001600160a01b03165f525f516020615fa35f395f51905f5260205260405f2090565b92611978565b50828410611993565b6001915014155f61198c565b506119fb61058582613e5e565b611939565b346101e35760203660031901126101e357602060043561031f6001604051921b1615158152f35b634e487b7160e01b5f52601160045260245ffd5b906177398202918083046177391490151715611a5357565b611a27565b906041116101e35790604190565b90929192836041116101e35783116101e357604101916040190190565b906014116101e35790601490565b90929192836014116101e35783116101e357601401916013190190565b906004116101e35790600490565b90929192836004116101e35783116101e357600401916003190190565b90929192836005116101e35783116101e357600501916004190190565b356bffffffffffffffffffffffff19811692919060148210611b16575050565b6bffffffffffffffffffffffff1960149290920360031b82901b16169150565b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff821117611b6657604052565b611b36565b90601f8019910116810190811067ffffffffffffffff821117611b6657604052565b908160209103126101e35751611ba2816105d6565b90565b611ba293926001600160a01b036060931682526020820152816040820152019061101d565b6040513d5f823e3d90fd5b918015611c8e575b90611c17611c3e93611c118480611c0b610429611c05611bff60209a8a611a83565b90611af6565b60601c90565b95611a91565b91612c84565b91906001600160a01b03604051809681958294637aa8f17760e11b84523360048501611ba5565b0392165afa5f9181611c5d575b50611ba257506001600160e01b031990565b611c8091925060203d602011611c87575b611c788183611b6b565b810190611b8d565b905f611c4b565b503d611c6e565b611c9c61ffff821904611a3b565b8303611bdd5790611ba292612b0b565b15611cb357565b7fac52ccbe000000000000000000000000000000000000000000000000000000005f5260045ffd5b6040519061028161012083611b6b565b60405190610281606083611b6b565b60405190610281604083611b6b565b67ffffffffffffffff8111611b6657601f01601f191660200190565b929192611d3182611d09565b91611d3f6040519384611b6b565b8294818452818301116101e3578281602093845f960137010152565b9080601f830112156101e357816020611ba293359101611d25565b919091610120818403126101e357611d8c611cdb565b92611d9682610276565b845260208201356020850152604082013567ffffffffffffffff81116101e35781611dc2918401611d5b565b6040850152606082013567ffffffffffffffff81116101e35781611de7918401611d5b565b60608501526080820135608085015260a082013560a085015260c082013560c085015260e082013567ffffffffffffffff81116101e35781611e2a918401611d5b565b60e085015261010082013567ffffffffffffffff81116101e357611e4e9201611d5b565b610100830152565b15611e5d57565b7fae4edb1b000000000000000000000000000000000000000000000000000000005f5260045ffd5b903590601e19813603018212156101e3570180359067ffffffffffffffff82116101e3576020019181360383136101e357565b908160209103126101e3575190565b80516001600160a01b03168252611ba29160208201516020820152610100611f48611f18611f066040860151610120604087015261012086019061101d565b6060860151858203606087015261101d565b6080850151608085015260a085015160a085015260c085015160c085015260e085015184820360e086015261101d565b9201519061010081840391015261101d565b929190611f71602091604086526040860190611ec7565b930152565b908160209103126101e3575180151581036101e35790565b60405163d60b347f60e01b81523060048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156104ee575f91611ff4575b508015611feb5790565b50611ba26130c4565b612016915060203d60201161201c575b61200e8183611b6b565b810190611f76565b5f611fe1565b503d612004565b1561202a57565b7f8baa579f000000000000000000000000000000000000000000000000000000005f5260045ffd5b81601f820112156101e35780519061206982611d09565b926120776040519485611b6b565b828452602083830101116101e357815f9260208093018386015e8301015290565b906020828203126101e357815167ffffffffffffffff81116101e357611ba29201612052565b908060209392818452848401375f828201840152601f01601f1916010190565b611ba293926001600160a01b03606093168252602082015281604082015201905f6120be565b906020611ba292818152019061101d565b1561211c57565b7fc970156c000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190916001600160a01b0381169061215d821515612115565b612169813b1515612115565b3055612194307f00000000000000000000000000000000000000000000000000000000000000001490565b610a2c576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016331480156122bb575b6121d490611cac565b6121dc613154565b612293573d5f526352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602060016004601d855afa510361228557807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a281817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5561226f57505050565b815f926040519485378338925af4156108925750565b6355299b496001526004601dfd5b7fdc3b8379000000000000000000000000000000000000000000000000000000005f5260045ffd5b503330146121cb565b35611ba281610265565b156122d65750565b7f41c38b30000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b1561230a575050565b6001600160a01b0392507fbe601672000000000000000000000000000000000000000000000000000000005f526004521660245260445ffd5b90620151808201809211611a5357565b91908201809211611a5357565b908092918237015f815290565b3d15612397573d9061237e82611d09565b9161238c6040519384611b6b565b82523d5f602084013e565b606090565b6123aa906060810190611e85565b806004116101e3576040515f9283929060031982019060040183378101826003198201528160031991030190305af46123e161236d565b50156123e957565b63acfdb4445f526004601cfd5b90917f341347516a9de374859dfda710fa4828b2d48cb57d4fbe4c1149612b8e02276e9361242b8484610ad884868484612a56565b600183036124675761243d9184613ba1565b612445613c9b565b604080519182526001600160a01b03909216602082015290819081015b0390a1565b6002830361247e576124799184613a2c565b612445565b600383036125625761255b916125266125559261251e6040516124a081611b4a565b5f81525f60208201526124bf61060c6124b98588611aae565b90612a20565b8151815460209093015174ff000000000000000000000000000000000000000060589190911c167fffffffffffffffffffffff0000000000000000000000000000000000000000009093166001600160a01b0390911617919091179055565b805a93611abc565b9290612547604051948592638a91b0e360e01b602085015260248401613450565b03601f198101845283611b6b565b84614602565b5050612445565b60048314801561258a575b612578575050612445565b612583918385613461565b5f8061255b565b50600260071984011061256d565b91908260081b926125b1816001600160f81b0319161590565b156125c2575090611ba29291614047565b6001600160f81b03198116600160f81b036125e3575090611ba29291613f80565b6001600160f81b0319808216036126cd5750906125ff916153b8565b9091612609613eb6565b936001600160f81b0319811661263b575091612624926153f7565b61262d82613f39565b5261263781613f39565b5090565b6001600160f81b03198116600160f81b036126b15750818361265c926153d2565b61266585613f39565b521561267057505090565b7f5bd4c60b4b38b664d8fb5944eb974e3d85083d79afe5ce934ccabcc913707c10916126ab61269e85613f39565b5160405193849384613f5a565b0390a190565b6308c3ee0360e11b5f526001600160f81b03191660045260245ffd5b6001600160f81b031990632e5bf3f960e21b5f521660045260245ffd5b91908260081b92612703816001600160f81b0319161590565b15612714575090610281929161417d565b6001600160f81b03198116600160f81b0361273557509061028192916140f4565b6001600160f81b0319808216036126cd575090612751916153b8565b929091906001600160f81b0319811661277f57505f9083604051938437838338925af4156108925701604052565b9192916001600160f81b03198116600160f81b036127e0575081836127a3926153d2565b9290156127af57505050565b6124627f5bd4c60b4b38b664d8fb5944eb974e3d85083d79afe5ce934ccabcc913707c109360405193849384613f5a565b6001600160f81b0319906308c3ee0360e11b5f521660045260245ffd5b5f356001600160e01b0319811690612846826001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b546128656001600160a01b0382169160581b6001600160f81b03191690565b906001600160a01b0381166128ae57505060e01c63bc197c81811463f23a6e6182141763150b7a028214176128a557506308c63e275f526020526024601cfd5b6020526020603cf35b5f516020615f835f395f51905f52546001600160a01b03168015159260609290846129ca575b6001600160f81b03198116607f60f91b0361297857505f80916128f56155f2565b90602082519201905afa9261290861236d565b935b156129705761291c575b825160208401f35b803b156101e3576129475f92918392604051948580948193630b9dfbed60e11b835260048301612104565b03925af180156104ee5761295c575b80612914565b8061099d5f61296a93611b6b565b81612956565b835160208501fd5b6001600160f81b031981166129ae57505f80916129936155f2565b906020825192019034905af1926129a861236d565b9361290a565b632e5bf3f960e21b5f526001600160f81b03191660045260245ffd5b925060405163d68f602560e01b81525f81806129eb363433600485016120de565b038183875af19081156104ee575f91612a06575b50926128d4565b612a1a91503d805f833e6109bc8183611b6b565b856129ff565b919091356001600160e01b031981169260048110612a3c575050565b6001600160e01b0319929350829060040360031b1b161690565b92909160018403612a6d575050611ba29150613e1e565b60028403612a81575050611ba29150613e5e565b60038403612ab057611ba2935060048210612aa857612aa3916124b991611aae565b614273565b50505f614273565b505090600481145f14612ade57505f516020615f835f395f51905f52546001600160a01b0391821691161490565b600260071982011015612b05576001600160a01b03612afd8192614210565b921691161490565b50505f90565b60015f9081525f516020615f635f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7546001600160a01b031694939291905b6001600160a01b0386168015159081612bd1575b5015612b8957612b7d848484612b83948a6142bf565b95614397565b94612b53565b612bb59495507f00000000000000000000000000000000000000000000000000000000000000006142bf565b6001600160e01b03198116611ba257506001600160e01b031990565b6001915014155f612b67565b6001600160a01b038116612c1057507f000000000000000000000000000000000000000000000000000000000000000090565b612c1981613e1e565b15612c215790565b636859e01e5f526020526024601cfd5b9190916040818403126101e357805192602082015167ffffffffffffffff81116101e357611ba29201612052565b611ba294926001600160a01b03606093168252602082015281604082015201916120be565b7f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f06549293926001600160a01b031680612cc5575090611ba291933691611d25565b6040517f7a0468b7000000000000000000000000000000000000000000000000000000008152945f93869384928392612d02923360048601612c5f565b03915afa9182156104ee575f905f93612d1a57509190565b90506102ed9192503d805f833e612d318183611b6b565b810190612c31565b9060f911612e43578035906020810135810190602082019135906040810135019360208501943593612d6c368486611d25565b602081519101205f528060a01c602052612dfb6105856001600160a01b0360405f201692835f526001600160a01b0360205f2091608060205216601f526305d78094600b52601960272090604051915f52601b602052846040526060526020604060805f60015afa505f6060523d6060185190604052612df330916001600160a01b031690565b143015151690565b612e3c575b8015612e2f575f527f4f058962bce244bca6c9be42f256083afc66f1f63a1f9a04e31a3042311af38d60205fa1565b63e483bbcb5f526004601cfd5b505f612e00565b63aed595955f526004601cfd5b90601811612eda575f8160208083940135820180358060405193849301833781018481520391355af4612e8161236d565b5015612ecd57612e8f613154565b15612e9657565b612e9e611f8e565b15612ea557565b7f7556034e000000000000000000000000000000000000000000000000000000005f5260045ffd5b63315927c55f526004601cfd5b7ff9c42c60000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001600160a01b03611ba29593606093835216602082015281604082015201916120be565b9290803560601c601482013590603483013560e01c916038840193838101603881013560e01c976038838a603c850194010301998160048c01116101e357612fa387612f998a9460048f612fa89901019e6003199103019c80612f93610429611c05611bff848c611a83565b97611a91565b949093888a6143dc565b61442a565b1561300e57303b156101e357612fed5f9360405195869485947f9517e29f00000000000000000000000000000000000000000000000000000000865260048601612f02565b038183305af180156104ee576130005750565b8061099d5f61028193611b6b565b6346fdc3335f526004601cfd5b92916001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f05541680155f1461305a575050610100015190565b60405f80948297949751978895869485937fe24f8f930000000000000000000000000000000000000000000000000000000085526130a46004860191606083526060830190611ec7565b936020820152015203925af19182156104ee575f905f93612d1a57509190565b60015f525f516020615f635f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7546001600160a01b031615158061310e5790565b5060015f525f516020615fa35f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13546001600160a01b0316151590565b5f906017303b1461316157565b905060035f80303c5f5160e81c62ef01001490565b7f90b772c2cb8a51aa7a8a65fc23543c6d022d5b3f8e2b92eed79fba7eef8293005c15612e4357565b91909160405192805180604014613204576041146131c957505050505b638baa579f5f526004601cfd5b60209160608201515f1a835260408201516060525b5f5201516040526020600160805f825afa51915f6060526040523d6102815750506131bc565b506020917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040830151601b8160ff1c018552166060526131de565b1561324757565b7f756688fe000000000000000000000000000000000000000000000000000000005f5260045ffd5b1561327657565b7fbf6b5843000000000000000000000000000000000000000000000000000000005f5260045ffd5b6020916001600160a01b03916133e061340195806132c5610429611c05611bff8489611a83565b946133db6107e361336e6132d8886122c4565b8b8901359861336660606132f26105216040850185611e85565b8f81519101209201359a6133588c60405194602093869485019788909493926080926001600160a01b0360a08401977fd3ddfc12654178cc44d4a7b6b969cfdce7ffe6342326ba37825314cffa0fba9c8552166020840152604083015260608201520152565b03601f198101835282611b6b565b5190206144af565b966133ae6133a961058561078e845f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0760205260405f2090565b613240565b5f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0760205260405f2090565b611a91565b93909160405196879586948593637aa8f17760e11b85523060048601612c5f565b0392165afa80156104ee576001600160e01b0319630b135d3f60e11b91610281935f91613431575b50161461326f565b61344a915060203d602011611c8757611c788183611b6b565b5f613429565b916020611ba29381815201916120be565b6134c4939291600481036134c857505f516020615f835f395f51905f5280546001600160a01b03191690555b6134bf5a926134b1604051958692638a91b0e360e01b602085015260248401613450565b03601f198101855284611b6b565b614602565b5050565b60026007198201106134db575b5061348d565b6008810361351a57507f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0680546001600160a01b03191690555b5f6134d5565b600903613514577f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0580546001600160a01b0319169055613514565b634e487b7160e01b5f52603260045260245ffd5b919081101561358b5760051b81013590605e19813603018212156101e3570190565b613555565b903590601e19813603018212156101e3570180359067ffffffffffffffff82116101e357602001918160051b360383136101e357565b604051906135d5602083611b6b565b5f808352366020840137565b91905f5b8181106135f25750509050565b6135fd818386613569565b61360a6020820182613590565b908235613616816105d6565b61361e6146a8565b506040516001600160e01b031990911660208201525f9283916136448160248101613358565b925f91825b8184106136f057505050509160019493916136d893613678613669611ceb565b6001600160a01b039094168452565b602083015260408201526136966107a482516001600160a01b031690565b156136de576136c4816136b36136d093516001600160a01b031690565b906040602082015191015191615995565b915b6040810190613590565b309291614795565b016135e5565b506136d06136ea6135c6565b916136c6565b6137066137018584869a979a6146d8565b61574c565b9361371a6137158985876146d8565b61471d565b6137238161470e565b61385257506002613740602061373a8a86886146d8565b0161471d565b6137498161470e565b146137e6576001811661377a576137716107a46001809317956020808251830101910161475e565b965b0192613649565b604051634d038a6760e11b815260206004820152602660248201527f54415247455420706172616d20747970652063616e206f6e6c7920626520736560448201527f74206f6e636500000000000000000000000000000000000000000000000000006064820152608490fd5b604051635107885760e11b815260206004820152603b60248201527f42414c414e434520666574636865722074797065206973206e6f74207375707060448201527f6f7274656420666f722054415247455420706172616d207479706500000000006064820152608490fd5b969460016138646137158886886146d8565b61386d8161470e565b0361390657506002811661389a576138946002600192179460208082518301019101611eb8565b94613773565b604051634d038a6760e11b815260206004820152602560248201527f56414c554520706172616d20747970652063616e206f6e6c792062652073657460448201527f206f6e63650000000000000000000000000000000000000000000000000000006064820152608490fd5b9495600261391b6137158986889996996146d8565b6139248161470e565b0361393b576001916139359161473c565b95613773565b604051635107885760e11b815260206004820152601260248201527f496e76616c696420706172616d207479706500000000000000000000000000006044820152606490fd5b60405161398f604082611b6b565b600581527f4e657875730000000000000000000000000000000000000000000000000000006020820152906040516139c8604082611b6b565b600581527f312e332e31000000000000000000000000000000000000000000000000000000602082015290565b9190916040818403126101e3578035613a0d81610265565b92602082013567ffffffffffffffff81116101e357611ba29201611d5b565b90613a42906001600160a01b03938101906139f5565b92166001600160a01b03821680158015613b97575b613b84576001600160a01b03613a8b835f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b541603613b68579181613b0c6134c494613af1613acc610db56134b1975f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b915f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b906001600160a01b03166001600160a01b0319825416179055565b613b47613b37825f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b6001600160a01b03198154169055565b6134bf5a92604051948591638a91b0e360e01b602084015260248301612104565b637c84ecfb60e01b5f526001600160a01b03821660045260245ffd5b50637c84ecfb60e01b5f5260045260245ffd5b5060018114613a57565b90613bb7906001600160a01b03938101906139f5565b92166001600160a01b03821680158015613c91575b613b84576001600160a01b03613c00835f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b541603613b68579181613c666134c494613af1613c41610db56134b1975f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b915f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b613b47613b37825f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b5060018114613bcc565b613ca6610585613154565b80613da4575b613cb257565b60015f525f516020615f635f395f51905f52602052613cf07ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7610db5565b6001600160a01b03811680151580613d99575b15613d885760405163d60b347f60e01b815230600482015290602090829060249082905afa9081156104ee575f91613d6a575b50613d4957613d4490614397565b613cf0565b6001600160a01b036001915b1614613d5d57565b63cc319d845f526004601cfd5b613d82915060203d811161201c5761200e8183611b6b565b5f613d36565b506001600160a01b03600191613d55565b506001811415613d03565b5060405163d60b347f60e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156104ee575f91613dff575b5015613cac565b613e18915060203d60201161201c5761200e8183611b6b565b5f613df8565b6001600160a01b031680600114159081613e36575090565b90505f525f516020615f635f395f51905f526020526001600160a01b0360405f205416151590565b6001600160a01b031680600114159081613e76575090565b90505f525f516020615fa35f395f51905f526020526001600160a01b0360405f205416151590565b67ffffffffffffffff8111611b665760051b60200190565b60408051909190613ec78382611b6b565b6001815291601f1901825f5b828110613edf57505050565b806060602080938501015201613ed3565b90613efa82613e9e565b613f076040519182611b6b565b8281528092613f18601f1991613e9e565b01905f5b828110613f2857505050565b806060602080938501015201613f1c565b80511561358b5760200190565b805182101561358b5760209160051b010190565b91611ba29391613f72916040855260408501916120be565b91602081840391015261101d565b90613f8a9161542a565b916001600160f81b031981166140075750613fa482613ef0565b915f5b818110613fb45750505090565b80613feb613fc56001938587613569565b8035613fd081610265565b613fe36020830135926040810190611e85565b9290916155a9565b613ff58287613f46565b526140008186613f46565b5001613fa7565b9291906001600160f81b03198416600160f81b0361402a57611ba29293506154aa565b6001600160f81b0319846308c3ee0360e11b5f521660045260245ffd5b906140549193929361555a565b91909261405f613eb6565b956001600160f81b0319811661409157509061407b93916155a9565b61408483613f39565b5261408e82613f39565b50565b6001600160f81b03198116600160f81b036127e05750836140b3928492615582565b6140bc86613f39565b52156140c6575050565b7fb5282692b8c578af7fb880895d599035496b5e64d1f14bf428a1ed3bc406f6629161246261269e86613f39565b906140fe9161542a565b90916001600160f81b0319811661415b57505f5b81811061411e57505050565b8061415561412f6001938587613569565b803561413a81610265565b61414d6020830135926040810190611e85565b9290916155d6565b01614112565b92916001600160f81b03198416600160f81b0361402a5761408e9293506154aa565b6001600160f81b0319916141909161555a565b9094909391929116806141a957509061028193916155d6565b600160f81b81036141fe5750836141c1928492615582565b9290156141cd57505050565b6124627fb5282692b8c578af7fb880895d599035496b5e64d1f14bf428a1ed3bc406f6629360405193849384613f5a565b6308c3ee0360e11b5f5260045260245ffd5b600803614245576001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f06541690565b6001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f05541690565b6001600160a01b036142b781926001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b541691161490565b6142ef936001600160a01b0360209496939660405196879586948593637aa8f17760e11b85523360048601612c5f565b0392165afa9081156104ee575f91614378575b507f77390000000000000000000000000000000000000000000000000000000000007fffff0000000000000000000000000000000000000000000000000000000000008216148061435b575b614356575090565b905090565b506001600160e01b031982166001600160e01b031982161161434e565b614391915060203d602011611c8757611c788183611b6b565b5f614302565b6001600160a01b031680156143ca575f525f516020615f635f395f51905f526020526001600160a01b0360405f20541690565b637c84ecfb60e01b5f5260045260245ffd5b92909160a09492604051947ff6c866c1cd985ce61f030431e576c0e82887de0643dfa8a2e6efc3463e638ed08652602086015260408501526060840152818484019182372060808201522090565b92602092916001600160a01b03614443614461966144af565b60405196879586948593637aa8f17760e11b85523060048601612c5f565b0392165afa5f918161448e575b5061447857505f90565b6001600160e01b031916630b135d3f60e11b1490565b6144a891925060203d602011611c8757611c788183611b6b565b905f61446e565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000030147f00000000000000000000000000000000000000000000000000000000000000004614161561453a575b6719010000000000005f52601a52603a526042601820905f603a52565b5060a06040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527f000000000000000000000000000000000000000000000000000000000000000060208201527f000000000000000000000000000000000000000000000000000000000000000060408201524660608201523060808201522061451d565b6001600160a01b0361028191166001600160a01b03166001600160a01b03195f516020615f835f395f51905f525416175f516020615f835f395f51905f5255565b92915f919082918261461381611d09565b966146216040519889611b6b565b818852601f1961463083611d09565b013660208a013760208451940192f13d80614653575b8084525f602085013e9190565b505f614646565b9061466482613e9e565b6146716040519182611b6b565b8281528092614682601f1991613e9e565b0190602036910137565b5f198114611a535760010190565b5f19810191908211611a5357565b604051906060820182811067ffffffffffffffff821117611b665760405260606040835f81525f60208201520152565b919081101561358b5760051b81013590607e19813603018212156101e3570190565b634e487b7160e01b5f52602160045260245ffd5b6003111561471857565b6146fa565b3560038110156101e35790565b805191908290602001825e015f815290565b610281906125476147589493604051958693602085019061472a565b9061472a565b908160209103126101e35751611ba281610265565b919081101561358b5760051b81013590603e19813603018212156101e3570190565b91905f5b8181106147a7575050505050565b6147b2818386614773565b90856147bd836159cd565b6147c6816159c3565b6147f8576147f2906147de8460206001960190611e85565b506040810135908760208201359135615e8e565b01614799565b50906001614805826159cd565b61480e816159c3565b0361489f57806020614821920190611e85565b50905f806148416040850135850160405191828260208294359101612360565b039060208601355afa61485261236d565b901561487757866148729184608060019601359160608201359135615e8e565b6147f2565b7f6e47f619000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fe2121978000000000000000000000000000000000000000000000000000000005f5260045ffd5b60015f525f516020615fa35f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13546001600160a01b03166149ad5760015f525f516020615fa35f395f51905f5260205261493a60405f2060016001600160a01b0319825416179055565b60015f525f516020615f635f395f51905f526020526001600160a01b037ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c754166149ad5760015f525f516020615f635f395f51905f5260205261028160405f2060016001600160a01b0319825416179055565b7f53c85e66000000000000000000000000000000000000000000000000000000005f5260045ffd5b919081101561358b5760051b0190565b9082101561358b576102ed9160051b810190611e85565b9150614a1e908035810191602083019235916020810135019060208201913590565b929391808403614afd575f5b818110614a3957505050505050565b80614a4760019284896149d5565b35828103614a6b5750614a65614a5e8288876149e5565b9087615260565b01614a2a565b60028103614a8e5750614a89614a828288876149e5565b9087615160565b614a65565b60038103614aac5750614a89614aa58288876149e5565b9087614e7c565b60048103614aca5750614a89614ac38288876149e5565b9087614d0f565b856002600719830110614adf575b5050614a65565b614af691614aee848a896149e5565b929091614b25565b5f85614ad8565b7fb4fa3fb3000000000000000000000000000000000000000000000000000000005f5260045ffd5b9291906001600160a01b03614b4e6001600160a01b035f516020615f835f395f51905f52541690565b1680614b5e575061028193614c3b565b916040939193519363d68f602560e01b85525f8580614b82363433600485016120de565b038183885af19485156104ee575f95614bde575b50614ba2939495614c3b565b803b156101e357614bcd5f92918392604051948580948193630b9dfbed60e11b835260048301612104565b03925af180156104ee576130005750565b614ba2949550614bf7903d805f833e6109bc8183611b6b565b9493614b96565b15614c065750565b6001600160a01b03907fc689cd97000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60405163ecd0596160e01b8152600481018290526001600160a01b038316949290602081602481895afa9081156104ee575f91614cf0575b5015614cc85781614c99614c89614c9e94614210565b6001600160a01b03811615614bfe565b61561f565b823b156101e357614bcd925f92836040518096819582946306d61fe760e41b845260048401613450565b7fe27c1dd8000000000000000000000000000000000000000000000000000000005f5260045ffd5b614d09915060203d60201161201c5761200e8183611b6b565b5f614c73565b91906001600160a01b03614d376001600160a01b035f516020615f835f395f51905f52541690565b1680614d47575061028192614de6565b90916040519263d68f602560e01b84525f8480614d69363433600485016120de565b038183875af19384156104ee575f94614d89575b50614ba2929394614de6565b614ba2939450614da2903d805f833e6109bc8183611b6b565b9392614d7d565b15614db15750565b6001600160a01b03907f741cbe03000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60405163ecd0596160e01b81526004808201526001600160a01b038216939190602081602481885afa9081156104ee575f91614e5d575b5015614cc857614c9e90614e58614e486001600160a01b035f516020615f835f395f51905f52541690565b6001600160a01b03811615614da9565b6145c1565b614e76915060203d60201161201c5761200e8183611b6b565b5f614e1d565b91906001600160a01b03614ea46001600160a01b035f516020615f835f395f51905f52541690565b1680614eb4575061028192614fc1565b90916040519263d68f602560e01b84525f8480614ed6363433600485016120de565b038183875af19384156104ee575f94614ef6575b50614ba2929394614fc1565b614ba2939450614f0f903d805f833e6109bc8183611b6b565b9392614eea565b906004101561358b5760040190565b15614f2c57565b7f867a1dcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b15614f5b57565b7fc001660b000000000000000000000000000000000000000000000000000000005f5260045ffd5b15614f8b5750565b6001600160e01b0319907fa56a04dd000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90916001600160a01b0382169160405163ecd0596160e01b815260208180614ff160048201906003602083019252565b0381875afa9081156104ee575f91615141575b5015614cc8576124bf8461060c6150766105218680615054615047615039836150336124b96150e59e8c611aae565b99614f16565b356001600160f81b03191690565b6001600160f81b03191690565b9a6001600160f81b03198c1615801561512a575b61507190614f25565b611ad9565b966150a56001600160e01b031984166306d61fe760e41b8114908115615119575b8115615110575b5015614f54565b6150ba836150b5610585826159da565b614f83565b6150d46150c5611cfa565b6001600160a01b039096168652565b6001600160f81b0319166020850152565b803b156101e357614bcd5f929183926040519485809481936306d61fe760e41b835260048301612104565b9050155f61509e565b638a91b0e360e01b81149150615097565b50607f60f91b6001600160f81b03198d1614615068565b61515a915060203d60201161201c5761200e8183611b6b565b5f615004565b91906001600160a01b036151886001600160a01b035f516020615f835f395f51905f52541690565b16806151985750610281926151fa565b90916040519263d68f602560e01b84525f84806151ba363433600485016120de565b038183875af19384156104ee575f946151da575b50614ba29293946151fa565b614ba29394506151f3903d805f833e6109bc8183611b6b565b93926151ce565b60405163ecd0596160e01b8152600260048201526001600160a01b038216939190602081602481885afa9081156104ee575f91615241575b5015614cc857614c9e90615a24565b61525a915060203d60201161201c5761200e8183611b6b565b5f615232565b91906001600160a01b036152886001600160a01b035f516020615f835f395f51905f52541690565b16806152985750610281926152fa565b90916040519263d68f602560e01b84525f84806152ba363433600485016120de565b038183875af19384156104ee575f946152da575b50614ba29293946152fa565b614ba29394506152f3903d805f833e6109bc8183611b6b565b93926152ce565b60405163ecd0596160e01b8152600160048201526001600160a01b038216939190602081602481885afa9081156104ee575f91615399575b5015614cc8576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016841461537157614c9e90615b48565b7fabc3af79000000000000000000000000000000000000000000000000000000005f5260045ffd5b6153b2915060203d60201161201c5761200e8183611b6b565b5f615332565b91816014116101e357823560601c92601401916013190190565b5f9192806040519485378338925af4913d82523d5f602084013e60203d830101604052565b5f919392806040519586378438925af415615421573d82523d5f602084013e60203d830101604052565b503d5f823e3d90fd5b909181359182810193601f199101016020840193803593828560051b8301119060401c1761549d578361545b575050565b835b5f190160208160051b8301013580830160608101908135809101918680602080860135809601011191111792171760401c1761549d578061545d57505050565b63ba597e7e5f526004601cfd5b9190916154b683613ef0565b925f5b8181106154c557505050565b806154d36001928486613569565b8035906154df82610265565b6154fe602082013560408301936154f68585611e85565b929091615582565b615508858b613f46565b5215615517575b5050016154b9565b7fb5282692b8c578af7fb880895d599035496b5e64d1f14bf428a1ed3bc406f6629161554291611e85565b9061555061269e858b613f46565b0390a15f8061550f565b90806014116101e357813560601c92603482106101e357601483013592603401916033190190565b905f928491604051958692833738935af1913d82523d5f602084013e60203d830101604052565b90925f92819594604051968792833738935af115615421573d82523d5f602084013e60203d830101604052565b8380935f93604051958692833738935af1156108925701604052565b60405190602036830101604052816014360181525f602036920137604051601481016040523360601b9052565b6008810361568f57506001600160a01b0361028191166001600160a01b03166001600160a01b03197f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f065416177f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0655565b6009146156995750565b6001600160a01b0361028191166001600160a01b03166001600160a01b03197f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f055416177f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0555565b1561570757565b6064604051635107885760e11b815260206004820152601860248201527f496e76616c696420706172616d44617461206c656e67746800000000000000006044820152fd5b6060906020810161575c8161471d565b6157658161470e565b6157a95750611ba291508060406105219201906157a46157858383611e85565b61579f6157956060860186613590565b9390923691611d25565b615d32565b611e85565b60016157b48261471d565b6157bd8161470e565b03615827575090505f806157d46040840184611e85565b50604051806157ee81602085810135860180359101612360565b0391355afa906157fc61236d565b911561581a57615813816060611ba2930190613590565b9083615d32565b636533cc8d5f526004601cfd5b61583260029161471d565b61583b8161470e565b0361594f57602861585a6158526040840184611e85565b909214615700565b6014810135831c9035831c806158c75750906158b3613358936158ad611ba29431935b604051926158a58461589788602083019190602083019252565b03601f198101865285611b6b565b810190613590565b91615d32565b604051928391602083019190602083019252565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03929092166004830152909190602090839060249082905afa80156104ee57613358936158ad6158b392611ba2955f91615930575b509361587d565b615949915060203d6020116104e7576104d98183611b6b565b5f615929565b604051635107885760e11b815260206004820152601a60248201527f496e76616c696420706172616d206665746368657220747970650000000000006044820152606490fd5b9291905f91604051943892602083519301915af115615421573d82523d5f602084013e60203d830101604052565b6002111561471857565b3560028110156101e35790565b615a1d6001600160a01b03916001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b5416151590565b6001600160a01b03811680158015615b3e575b6143ca575f9081525f516020615fa35f395f51905f5260205260409020546001600160a01b0316615b0a5760015f525f516020615fa35f395f51905f5260205261028190615acf615aa77fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13610db5565b613af1835f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b60015f525f516020615fa35f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13613af1565b7f40d3d1a4000000000000000000000000000000000000000000000000000000005f526001600160a01b031660045260245ffd5b5060018114615a37565b6001600160a01b03811680158015615c2e575b6143ca575f9081525f516020615f635f395f51905f5260205260409020546001600160a01b0316615b0a5760015f525f516020615f635f395f51905f5260205261028190615bf3615bcb7ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7610db5565b613af1835f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b60015f525f516020615f635f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7613af1565b5060018114615b5b565b6040813603126101e35760405190615c4f82611b4a565b803560048110156101e357825260208101359067ffffffffffffffff82116101e357615c7d91369101611d5b565b602082015290565b6004111561471857565b91908260409103126101e3576020825192015190565b15615cac57565b630a31844b60e41b5f52600360045260245ffd5b15615cc757565b630a31844b60e41b5f52600260045260245ffd5b15615ce257565b630a31844b60e41b5f52600160045260245ffd5b15615cfd57565b630a31844b60e41b5f525f60045260245ffd5b602081519101519060208110615d24575090565b5f199060200360031b1b1690565b9180615d3d57505050565b5f5b818110615d4c5750505050565b615d5f615d5a828486614773565b615c38565b9060208160051b86010151918051615d7681615c85565b615d7f81615c85565b615da257600192615d966020615d9c930151615d10565b14615cf6565b01615d3f565b60018151615daf81615c85565b615db881615c85565b03615ddc57600192615dd06020615dd7930151615d10565b1115615cdb565b615d9c565b60028151615de981615c85565b615df281615c85565b03615e1157600192615e0a6020615dd7930151615d10565b1015615cc0565b60038151615e1e81615c85565b615e2781615c85565b03615e6657600192615e496020615dd793015160208082518301019101615c8f565b908210159182615e5b575b5050615ca5565b111590505f80615e54565b7f2c50c45e000000000000000000000000000000000000000000000000000000005f5260045ffd5b929193905f5b848110615ea357505050505050565b60208160051b83010151906001600160a01b038716916040516020810190615edb81613358868b869091604092825260208201520190565b519020833b156101e3576040517fa39e0787000000000000000000000000000000000000000000000000000000008152600481019190915260248101919091526001600160a01b0385166044820152915f908390606490829084905af19182156104ee57600192615f4e575b5001615e94565b8061099d5f615f5c93611b6b565b5f615f4756fe0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f000bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f030bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f01a164736f6c634300081b000a0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0320000000000000000000000000000000055c766a7060797fbc7be40c08b296b7200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000
Deployed Bytecode
0x60806040526004361015610015575b366127fd57005b5f3560e01c80630a664dba146101d45780630b3dc354146101cf578063112d3a7d146101ca5780631626ba7e146101c557806319822f7c146101c0578063392e53cd146101bb578063481ddd23146101b65780634a58db19146101b15780634b6a1419146101ac5780634d44560d146101a75780634f1ef286146101a257806352d1902d1461019d5780635bfeadbb146101985780635faac46b146101935780637eba07b81461018e57806384b0196e146101895780638dd7712f146101845780639517e29f1461017f5780639cfd7cff1461017a578063a71763a814610175578063aaf10f4214610170578063b0d691fe1461016b578063c399ec8814610166578063d03c791414610161578063d691c9641461015c578063e9ae5c5314610157578063ea5f61d0146101525763f2dc691d0361000e57611a00565b611904565b61182e565b6116e7565b6115a7565b611551565b61150e565b6114b0565b6113c7565b611368565b6111ef565b6110fb565b611041565b610f00565b610d41565b610a39565b6109cb565b6108a3565b610808565b6106b7565b61067d565b6105e8565b6105bc565b610378565b610317565b6102f1565b610222565b6101e7565b5f9103126101e357565b5f80fd5b346101e3575f3660031901126101e35760206001600160a01b035f516020615f835f395f51905f5254166001600160a01b0360405191168152f35b346101e3575f3660031901126101e35760206040516001600160a01b037f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b72168152f35b6001600160a01b038116036101e357565b359061028182610265565b565b9181601f840112156101e35782359167ffffffffffffffff83116101e357602083818601950101116101e357565b60606003198201126101e357600435916024356102cd81610265565b916044359067ffffffffffffffff82116101e3576102ed91600401610283565b9091565b346101e357602061030d610304366102b1565b92919091612a56565b6040519015158152f35b346101e35760403660031901126101e35760043560243567ffffffffffffffff81116101e357602091610351610357923690600401610283565b91611bd5565b6001600160e01b031960405191168152f35b90816101209103126101e35790565b346101e35760603660031901126101e35760043567ffffffffffffffff81116101e3576103ac61047a913690600401610369565b602061042960243561043460443580956103f06001600160a01b037f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032163314611cac565b61042e6103fd3683611d76565b95869288810135906104138260031a60f81b1590565b156104f3575b5060401c6001600160a01b031690565b612bdd565b9361301b565b61010084929401525f6001600160a01b036040518098819682957f9700320300000000000000000000000000000000000000000000000000000000845260048401611f5a565b0393165af19081156104ee576104a9925f926104bd575b50806104ad575b506040519081529081906020820190565b0390f35b5f9081803892335af1505f610498565b8192506104e19060203d6020116104e7575b6104d98183611b6b565b810190611eb8565b91610491565b503d6104cf565b611bca565b600382901a60f81b600160f81b036105355761052161051a82610100610528940190611e85565b908a612f27565b3691611d25565b6101008501525b5f610419565b600382901a60f81b7f02000000000000000000000000000000000000000000000000000000000000001461056a575b5061052f565b6105ab61059f6105996105b69361058e610589610585611f8e565b1590565b611e56565b610100810190611e85565b90612d39565b93919290923691611d25565b610100880152612e50565b5f610564565b346101e3575f3660031901126101e357602061030d611f8e565b6001600160e01b03198116036101e357565b346101e35760203660031901126101e3576040602061064360043561060c816105d6565b6001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b82519061064f82611b4a565b54906001600160f81b03196001600160a01b0383169283835260581b16928391015282519182526020820152f35b5f3660031901126101e3575f388180347f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0325af1156101e357005b60203660031901126101e35760043567ffffffffffffffff81116101e3576106e66106f8913690600401610283565b9081813033036106fa575b5050612e50565b005b91610706939193613154565b156107f75750506107f06107e38261072d85610725816107b697611a58565b979093611a66565b9490956107b06107a461079e8998809a816040519182372095610754610589610585611f8e565b61079561058961058561078e8a5f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0960205260405f2090565b5460ff1690565b30943691611d25565b8561319f565b6001600160a01b031690565b14612023565b5f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0960205260405f2090565b805460ff19166001179055565b5f806106f1565b91509150610803613176565b6107f0565b60403660031901126101e35760043561082081610265565b5f7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032602435336001600160a01b03831614801561089a575b61086190611cac565b604051936014526034526f205c28780000000000000000000000008252604460108338935af115610892575f603452005b3d5f823e3d90fd5b50333014610858565b60403660031901126101e3576004356108bb81610265565b60243567ffffffffffffffff81116101e3576108db903690600401610283565b906001600160a01b036109026001600160a01b035f516020615f835f395f51905f52541690565b168061091257506106f892612144565b90916040519263d68f602560e01b84525f8480610934363433600485016120de565b038183875af19384156104ee575f946109a3575b50610954929394612144565b803b156101e35761097f5f92918392604051948580948193630b9dfbed60e11b835260048301612104565b03925af180156104ee5761098f57005b8061099d5f6106f893611b6b565b806101d9565b6109549394506109c4903d805f833e6109bc8183611b6b565b810190612098565b9392610948565b346101e3575f3660031901126101e357307f00000000000000000000000000000000561dd60aea485cdb26e4618b1e40fd6e03610a2c5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b639f03a0265f526004601cfd5b60403660031901126101e35760043567ffffffffffffffff81116101e3578060040190608060031982360301126101e35760243567ffffffffffffffff81116101e357610a8d610a94913690600401610283565b908461329e565b610ab06024820135926044610aa8826122c4565b930190611e85565b929060048214828115610cdb575b610ac7916122ce565b610add8383610ad887858484612a56565b612301565b610b17836001600160a01b03165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0460205260405f2090565b5480610ba8575050507f2841d18703faaff388732165e48fe431468531b1b1e626b1b7cbcbfc0d79c7409150610ba39042610b82826001600160a01b03165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0460205260405f2090565b55604080516001600160a01b03909216825242602083015290918291820190565b0390a1005b610bb56203f48082612353565b4210610c20575050507fcbd44a75f6935b5837022648b6c8487db984701200c5381c7c0f8c2b1d69b9da9150610ba39042610b82826001600160a01b03165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0460205260405f2090565b610c2990612343565b4210610cb3577f341347516a9de374859dfda710fa4828b2d48cb57d4fbe4c1149612b8e02276e93610c98915f610c90866001600160a01b03165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0460205260405f2090565b558385613461565b604080519182526001600160a01b03929092166020820152a1005b7f07f2f2d2000000000000000000000000000000000000000000000000000000005f5260045ffd5b60026007198201109150610abe565b90929192604082016040835281518091526020606084019201905f5b818110610d22575050506001600160a01b036020919416910152565b82516001600160a01b0316845260209384019390920191600101610d06565b346101e35760403660031901126101e357600435610d5e81610265565b6024359060016001600160a01b038216141580610eee575b610ed3578115610eab5790610d8a8161465a565b610dc2610db55f946001600160a01b03165f525f516020615f635f395f51905f5260205260405f2090565b546001600160a01b031690565b6001600160a01b0381168015159081610e9f575b5080610e96575b15610e3757610e2b610db582610e08610e3194610dfa8988613f46565b906001600160a01b03169052565b6001600160a01b03165f525f516020615f635f395f51905f5260205260405f2090565b9361468c565b92610dc2565b908360016001600160a01b038416141580610e8d575b610e63575b81526104a960405192839283610cea565b9150610e87610e7a610e748461469a565b83613f46565b516001600160a01b031690565b91610e52565b50801515610e4d565b50828410610ddd565b6001915014155f610dd6565b7ff7250817000000000000000000000000000000000000000000000000000000005f5260045ffd5b637c84ecfb60e01b5f526001600160a01b031660045260245ffd5b50610efb61058582613e1e565b610d76565b60203660031901126101e35760043567ffffffffffffffff81116101e357366023820112156101e357806004013567ffffffffffffffff81116101e3576024820191602436918360051b0101116101e357610f856001600160a01b037f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032163314611cac565b6001600160a01b03610fab6001600160a01b035f516020615f835f395f51905f52541690565b1680610fbb57506106f8916135e1565b906040519263d68f602560e01b84525f8480610fdc363433600485016120de565b038183875af19384156104ee575f94610ffb575b5090610954916135e1565b61095492919450611015903d805f833e6109bc8183611b6b565b939091610ff0565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346101e3575f3660031901126101e35761109c6110aa61105f613981565b6040929192519384937f0f00000000000000000000000000000000000000000000000000000000000000855260e0602086015260e085019061101d565b90838203604085015261101d565b4660608301523060808301525f60a083015281810360c083015260206060519182815201906080905f5b8181106110e2575050500390f35b82518452859450602093840193909201916001016110d4565b60403660031901126101e35760043567ffffffffffffffff81116101e357611127903690600401610369565b61115b6001600160a01b037f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032163314611cac565b6001600160a01b036111816001600160a01b035f516020615f835f395f51905f52541690565b168061119157506106f89061239c565b6040519163d68f602560e01b83525f83806111b1363433600485016120de565b038183865af19283156104ee575f936111cf575b506109549061239c565b6109549193506111e8903d805f833e6109bc8183611b6b565b92906111c5565b6111f8366102b1565b906001600160a01b039392937f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032163314801561135f575b61123890611cac565b6112436105856130c4565b611352575b6001600160a01b0384161561132a5760018303611296579061126a9184615260565b5f526020527fd21d0b289f126c4b473ea641963e766833c2f13866e4ff480abd787c100ef12360405fa1005b600283036112ae57906112a99184615160565b61126a565b600383036112c157906112a99184614e7c565b600483036112d457906112a99184614d0f565b6002600719840110156112ed57906112a9918484614b25565b826112fd57906112a991846149fc565b7f098312d2000000000000000000000000000000000000000000000000000000005f52600483905260245ffd5b7f5316c18d000000000000000000000000000000000000000000000000000000005f5260045ffd5b61135a6148c7565b611248565b5033301461122f565b346101e3575f3660031901126101e3576104a9604051611389604082611b6b565b601481527f6269636f6e6f6d792e6e657875732e312e332e31000000000000000000000000602082015260405191829160208352602083019061101d565b6113d0366102b1565b916001600160a01b037f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da03216331480156114a7575b61140d90611cac565b6001600160a01b036114336001600160a01b035f516020615f835f395f51905f52541690565b168061144357506106f8936123f6565b916040939193519363d68f602560e01b85525f8580611467363433600485016120de565b038183885af19485156104ee575f95611487575b506109549394956123f6565b6109549495506114a0903d805f833e6109bc8183611b6b565b949361147b565b50333014611404565b346101e3575f3660031901126101e3577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b03811615611506575b6020906001600160a01b0360405191168152f35b5030546114f2565b346101e3575f3660031901126101e35760206040516001600160a01b037f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032168152f35b346101e3575f3660031901126101e357306020526370a082315f52602080806024601c7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0325afa601f3d1116815102604051908152f35b346101e35760203660031901126101e3576104a96004358060081b6001600160f81b0319821615918215611640575b821561162b575b50816115f7575b5060405190151581529081906020820190565b6001600160f81b031981161591508115611613575b505f6115e4565b6001600160f81b031916600160f81b1490505f61160c565b6001600160f81b03199081161491505f6115dd565b6001600160f81b03198116600160f81b1492506115d6565b9060406003198301126101e357600435916024359067ffffffffffffffff82116101e3576102ed91600401610283565b602081016020825282518091526040820191602060408360051b8301019401925f915b8383106116ba57505050505090565b90919293946020806116d8600193603f19868203018752895161101d565b970193019301919392906116ab565b6116f036611658565b916116fa33613e5e565b15611802576001600160a01b036117256001600160a01b035f516020615f835f395f51905f52541690565b168061174657509161173a916104a993612598565b60405191829182611688565b90926040519263d68f602560e01b84525f8480611768363433600485016120de565b038183875af19384156104ee575f946117e2575b50611788929394612598565b90803b156101e3576117b45f93918492604051958680948193630b9dfbed60e11b835260048301612104565b03925af19182156104ee576104a9926117ce575b5061173a565b8061099d5f6117dc93611b6b565b5f6117c8565b6117889394506117fb903d805f833e6109bc8183611b6b565b939261177c565b7fb927fe5e000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b61183736611658565b9061186c6001600160a01b037f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032163314611cac565b6001600160a01b036118926001600160a01b035f516020615f835f395f51905f52541690565b16806118a257506106f8926126ea565b90916040519263d68f602560e01b84525f84806118c4363433600485016120de565b038183875af19384156104ee575f946118e4575b506109549293946126ea565b6109549394506118fd903d805f833e6109bc8183611b6b565b93926118d8565b346101e35760403660031901126101e35760043561192181610265565b6024359060016001600160a01b0382161415806119ee575b610ed3578115610eab579061194d8161465a565b611978610db55f946001600160a01b03165f525f516020615fa35f395f51905f5260205260405f2090565b6001600160a01b03811680151590816119e2575b50806119d9575b15610e3757610e2b610db5826119b06119d394610dfa8988613f46565b6001600160a01b03165f525f516020615fa35f395f51905f5260205260405f2090565b92611978565b50828410611993565b6001915014155f61198c565b506119fb61058582613e5e565b611939565b346101e35760203660031901126101e357602060043561031f6001604051921b1615158152f35b634e487b7160e01b5f52601160045260245ffd5b906177398202918083046177391490151715611a5357565b611a27565b906041116101e35790604190565b90929192836041116101e35783116101e357604101916040190190565b906014116101e35790601490565b90929192836014116101e35783116101e357601401916013190190565b906004116101e35790600490565b90929192836004116101e35783116101e357600401916003190190565b90929192836005116101e35783116101e357600501916004190190565b356bffffffffffffffffffffffff19811692919060148210611b16575050565b6bffffffffffffffffffffffff1960149290920360031b82901b16169150565b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff821117611b6657604052565b611b36565b90601f8019910116810190811067ffffffffffffffff821117611b6657604052565b908160209103126101e35751611ba2816105d6565b90565b611ba293926001600160a01b036060931682526020820152816040820152019061101d565b6040513d5f823e3d90fd5b918015611c8e575b90611c17611c3e93611c118480611c0b610429611c05611bff60209a8a611a83565b90611af6565b60601c90565b95611a91565b91612c84565b91906001600160a01b03604051809681958294637aa8f17760e11b84523360048501611ba5565b0392165afa5f9181611c5d575b50611ba257506001600160e01b031990565b611c8091925060203d602011611c87575b611c788183611b6b565b810190611b8d565b905f611c4b565b503d611c6e565b611c9c61ffff821904611a3b565b8303611bdd5790611ba292612b0b565b15611cb357565b7fac52ccbe000000000000000000000000000000000000000000000000000000005f5260045ffd5b6040519061028161012083611b6b565b60405190610281606083611b6b565b60405190610281604083611b6b565b67ffffffffffffffff8111611b6657601f01601f191660200190565b929192611d3182611d09565b91611d3f6040519384611b6b565b8294818452818301116101e3578281602093845f960137010152565b9080601f830112156101e357816020611ba293359101611d25565b919091610120818403126101e357611d8c611cdb565b92611d9682610276565b845260208201356020850152604082013567ffffffffffffffff81116101e35781611dc2918401611d5b565b6040850152606082013567ffffffffffffffff81116101e35781611de7918401611d5b565b60608501526080820135608085015260a082013560a085015260c082013560c085015260e082013567ffffffffffffffff81116101e35781611e2a918401611d5b565b60e085015261010082013567ffffffffffffffff81116101e357611e4e9201611d5b565b610100830152565b15611e5d57565b7fae4edb1b000000000000000000000000000000000000000000000000000000005f5260045ffd5b903590601e19813603018212156101e3570180359067ffffffffffffffff82116101e3576020019181360383136101e357565b908160209103126101e3575190565b80516001600160a01b03168252611ba29160208201516020820152610100611f48611f18611f066040860151610120604087015261012086019061101d565b6060860151858203606087015261101d565b6080850151608085015260a085015160a085015260c085015160c085015260e085015184820360e086015261101d565b9201519061010081840391015261101d565b929190611f71602091604086526040860190611ec7565b930152565b908160209103126101e3575180151581036101e35790565b60405163d60b347f60e01b81523060048201526020816024816001600160a01b037f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b72165afa9081156104ee575f91611ff4575b508015611feb5790565b50611ba26130c4565b612016915060203d60201161201c575b61200e8183611b6b565b810190611f76565b5f611fe1565b503d612004565b1561202a57565b7f8baa579f000000000000000000000000000000000000000000000000000000005f5260045ffd5b81601f820112156101e35780519061206982611d09565b926120776040519485611b6b565b828452602083830101116101e357815f9260208093018386015e8301015290565b906020828203126101e357815167ffffffffffffffff81116101e357611ba29201612052565b908060209392818452848401375f828201840152601f01601f1916010190565b611ba293926001600160a01b03606093168252602082015281604082015201905f6120be565b906020611ba292818152019061101d565b1561211c57565b7fc970156c000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190916001600160a01b0381169061215d821515612115565b612169813b1515612115565b3055612194307f00000000000000000000000000000000561dd60aea485cdb26e4618b1e40fd6e1490565b610a2c576001600160a01b037f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da03216331480156122bb575b6121d490611cac565b6121dc613154565b612293573d5f526352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602060016004601d855afa510361228557807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a281817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5561226f57505050565b815f926040519485378338925af4156108925750565b6355299b496001526004601dfd5b7fdc3b8379000000000000000000000000000000000000000000000000000000005f5260045ffd5b503330146121cb565b35611ba281610265565b156122d65750565b7f41c38b30000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b1561230a575050565b6001600160a01b0392507fbe601672000000000000000000000000000000000000000000000000000000005f526004521660245260445ffd5b90620151808201809211611a5357565b91908201809211611a5357565b908092918237015f815290565b3d15612397573d9061237e82611d09565b9161238c6040519384611b6b565b82523d5f602084013e565b606090565b6123aa906060810190611e85565b806004116101e3576040515f9283929060031982019060040183378101826003198201528160031991030190305af46123e161236d565b50156123e957565b63acfdb4445f526004601cfd5b90917f341347516a9de374859dfda710fa4828b2d48cb57d4fbe4c1149612b8e02276e9361242b8484610ad884868484612a56565b600183036124675761243d9184613ba1565b612445613c9b565b604080519182526001600160a01b03909216602082015290819081015b0390a1565b6002830361247e576124799184613a2c565b612445565b600383036125625761255b916125266125559261251e6040516124a081611b4a565b5f81525f60208201526124bf61060c6124b98588611aae565b90612a20565b8151815460209093015174ff000000000000000000000000000000000000000060589190911c167fffffffffffffffffffffff0000000000000000000000000000000000000000009093166001600160a01b0390911617919091179055565b805a93611abc565b9290612547604051948592638a91b0e360e01b602085015260248401613450565b03601f198101845283611b6b565b84614602565b5050612445565b60048314801561258a575b612578575050612445565b612583918385613461565b5f8061255b565b50600260071984011061256d565b91908260081b926125b1816001600160f81b0319161590565b156125c2575090611ba29291614047565b6001600160f81b03198116600160f81b036125e3575090611ba29291613f80565b6001600160f81b0319808216036126cd5750906125ff916153b8565b9091612609613eb6565b936001600160f81b0319811661263b575091612624926153f7565b61262d82613f39565b5261263781613f39565b5090565b6001600160f81b03198116600160f81b036126b15750818361265c926153d2565b61266585613f39565b521561267057505090565b7f5bd4c60b4b38b664d8fb5944eb974e3d85083d79afe5ce934ccabcc913707c10916126ab61269e85613f39565b5160405193849384613f5a565b0390a190565b6308c3ee0360e11b5f526001600160f81b03191660045260245ffd5b6001600160f81b031990632e5bf3f960e21b5f521660045260245ffd5b91908260081b92612703816001600160f81b0319161590565b15612714575090610281929161417d565b6001600160f81b03198116600160f81b0361273557509061028192916140f4565b6001600160f81b0319808216036126cd575090612751916153b8565b929091906001600160f81b0319811661277f57505f9083604051938437838338925af4156108925701604052565b9192916001600160f81b03198116600160f81b036127e0575081836127a3926153d2565b9290156127af57505050565b6124627f5bd4c60b4b38b664d8fb5944eb974e3d85083d79afe5ce934ccabcc913707c109360405193849384613f5a565b6001600160f81b0319906308c3ee0360e11b5f521660045260245ffd5b5f356001600160e01b0319811690612846826001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b546128656001600160a01b0382169160581b6001600160f81b03191690565b906001600160a01b0381166128ae57505060e01c63bc197c81811463f23a6e6182141763150b7a028214176128a557506308c63e275f526020526024601cfd5b6020526020603cf35b5f516020615f835f395f51905f52546001600160a01b03168015159260609290846129ca575b6001600160f81b03198116607f60f91b0361297857505f80916128f56155f2565b90602082519201905afa9261290861236d565b935b156129705761291c575b825160208401f35b803b156101e3576129475f92918392604051948580948193630b9dfbed60e11b835260048301612104565b03925af180156104ee5761295c575b80612914565b8061099d5f61296a93611b6b565b81612956565b835160208501fd5b6001600160f81b031981166129ae57505f80916129936155f2565b906020825192019034905af1926129a861236d565b9361290a565b632e5bf3f960e21b5f526001600160f81b03191660045260245ffd5b925060405163d68f602560e01b81525f81806129eb363433600485016120de565b038183875af19081156104ee575f91612a06575b50926128d4565b612a1a91503d805f833e6109bc8183611b6b565b856129ff565b919091356001600160e01b031981169260048110612a3c575050565b6001600160e01b0319929350829060040360031b1b161690565b92909160018403612a6d575050611ba29150613e1e565b60028403612a81575050611ba29150613e5e565b60038403612ab057611ba2935060048210612aa857612aa3916124b991611aae565b614273565b50505f614273565b505090600481145f14612ade57505f516020615f835f395f51905f52546001600160a01b0391821691161490565b600260071982011015612b05576001600160a01b03612afd8192614210565b921691161490565b50505f90565b60015f9081525f516020615f635f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7546001600160a01b031694939291905b6001600160a01b0386168015159081612bd1575b5015612b8957612b7d848484612b83948a6142bf565b95614397565b94612b53565b612bb59495507f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b726142bf565b6001600160e01b03198116611ba257506001600160e01b031990565b6001915014155f612b67565b6001600160a01b038116612c1057507f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b7290565b612c1981613e1e565b15612c215790565b636859e01e5f526020526024601cfd5b9190916040818403126101e357805192602082015167ffffffffffffffff81116101e357611ba29201612052565b611ba294926001600160a01b03606093168252602082015281604082015201916120be565b7f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f06549293926001600160a01b031680612cc5575090611ba291933691611d25565b6040517f7a0468b7000000000000000000000000000000000000000000000000000000008152945f93869384928392612d02923360048601612c5f565b03915afa9182156104ee575f905f93612d1a57509190565b90506102ed9192503d805f833e612d318183611b6b565b810190612c31565b9060f911612e43578035906020810135810190602082019135906040810135019360208501943593612d6c368486611d25565b602081519101205f528060a01c602052612dfb6105856001600160a01b0360405f201692835f526001600160a01b0360205f2091608060205216601f526305d78094600b52601960272090604051915f52601b602052846040526060526020604060805f60015afa505f6060523d6060185190604052612df330916001600160a01b031690565b143015151690565b612e3c575b8015612e2f575f527f4f058962bce244bca6c9be42f256083afc66f1f63a1f9a04e31a3042311af38d60205fa1565b63e483bbcb5f526004601cfd5b505f612e00565b63aed595955f526004601cfd5b90601811612eda575f8160208083940135820180358060405193849301833781018481520391355af4612e8161236d565b5015612ecd57612e8f613154565b15612e9657565b612e9e611f8e565b15612ea557565b7f7556034e000000000000000000000000000000000000000000000000000000005f5260045ffd5b63315927c55f526004601cfd5b7ff9c42c60000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001600160a01b03611ba29593606093835216602082015281604082015201916120be565b9290803560601c601482013590603483013560e01c916038840193838101603881013560e01c976038838a603c850194010301998160048c01116101e357612fa387612f998a9460048f612fa89901019e6003199103019c80612f93610429611c05611bff848c611a83565b97611a91565b949093888a6143dc565b61442a565b1561300e57303b156101e357612fed5f9360405195869485947f9517e29f00000000000000000000000000000000000000000000000000000000865260048601612f02565b038183305af180156104ee576130005750565b8061099d5f61028193611b6b565b6346fdc3335f526004601cfd5b92916001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f05541680155f1461305a575050610100015190565b60405f80948297949751978895869485937fe24f8f930000000000000000000000000000000000000000000000000000000085526130a46004860191606083526060830190611ec7565b936020820152015203925af19182156104ee575f905f93612d1a57509190565b60015f525f516020615f635f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7546001600160a01b031615158061310e5790565b5060015f525f516020615fa35f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13546001600160a01b0316151590565b5f906017303b1461316157565b905060035f80303c5f5160e81c62ef01001490565b7f90b772c2cb8a51aa7a8a65fc23543c6d022d5b3f8e2b92eed79fba7eef8293005c15612e4357565b91909160405192805180604014613204576041146131c957505050505b638baa579f5f526004601cfd5b60209160608201515f1a835260408201516060525b5f5201516040526020600160805f825afa51915f6060526040523d6102815750506131bc565b506020917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040830151601b8160ff1c018552166060526131de565b1561324757565b7f756688fe000000000000000000000000000000000000000000000000000000005f5260045ffd5b1561327657565b7fbf6b5843000000000000000000000000000000000000000000000000000000005f5260045ffd5b6020916001600160a01b03916133e061340195806132c5610429611c05611bff8489611a83565b946133db6107e361336e6132d8886122c4565b8b8901359861336660606132f26105216040850185611e85565b8f81519101209201359a6133588c60405194602093869485019788909493926080926001600160a01b0360a08401977fd3ddfc12654178cc44d4a7b6b969cfdce7ffe6342326ba37825314cffa0fba9c8552166020840152604083015260608201520152565b03601f198101835282611b6b565b5190206144af565b966133ae6133a961058561078e845f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0760205260405f2090565b613240565b5f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0760205260405f2090565b611a91565b93909160405196879586948593637aa8f17760e11b85523060048601612c5f565b0392165afa80156104ee576001600160e01b0319630b135d3f60e11b91610281935f91613431575b50161461326f565b61344a915060203d602011611c8757611c788183611b6b565b5f613429565b916020611ba29381815201916120be565b6134c4939291600481036134c857505f516020615f835f395f51905f5280546001600160a01b03191690555b6134bf5a926134b1604051958692638a91b0e360e01b602085015260248401613450565b03601f198101855284611b6b565b614602565b5050565b60026007198201106134db575b5061348d565b6008810361351a57507f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0680546001600160a01b03191690555b5f6134d5565b600903613514577f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0580546001600160a01b0319169055613514565b634e487b7160e01b5f52603260045260245ffd5b919081101561358b5760051b81013590605e19813603018212156101e3570190565b613555565b903590601e19813603018212156101e3570180359067ffffffffffffffff82116101e357602001918160051b360383136101e357565b604051906135d5602083611b6b565b5f808352366020840137565b91905f5b8181106135f25750509050565b6135fd818386613569565b61360a6020820182613590565b908235613616816105d6565b61361e6146a8565b506040516001600160e01b031990911660208201525f9283916136448160248101613358565b925f91825b8184106136f057505050509160019493916136d893613678613669611ceb565b6001600160a01b039094168452565b602083015260408201526136966107a482516001600160a01b031690565b156136de576136c4816136b36136d093516001600160a01b031690565b906040602082015191015191615995565b915b6040810190613590565b309291614795565b016135e5565b506136d06136ea6135c6565b916136c6565b6137066137018584869a979a6146d8565b61574c565b9361371a6137158985876146d8565b61471d565b6137238161470e565b61385257506002613740602061373a8a86886146d8565b0161471d565b6137498161470e565b146137e6576001811661377a576137716107a46001809317956020808251830101910161475e565b965b0192613649565b604051634d038a6760e11b815260206004820152602660248201527f54415247455420706172616d20747970652063616e206f6e6c7920626520736560448201527f74206f6e636500000000000000000000000000000000000000000000000000006064820152608490fd5b604051635107885760e11b815260206004820152603b60248201527f42414c414e434520666574636865722074797065206973206e6f74207375707060448201527f6f7274656420666f722054415247455420706172616d207479706500000000006064820152608490fd5b969460016138646137158886886146d8565b61386d8161470e565b0361390657506002811661389a576138946002600192179460208082518301019101611eb8565b94613773565b604051634d038a6760e11b815260206004820152602560248201527f56414c554520706172616d20747970652063616e206f6e6c792062652073657460448201527f206f6e63650000000000000000000000000000000000000000000000000000006064820152608490fd5b9495600261391b6137158986889996996146d8565b6139248161470e565b0361393b576001916139359161473c565b95613773565b604051635107885760e11b815260206004820152601260248201527f496e76616c696420706172616d207479706500000000000000000000000000006044820152606490fd5b60405161398f604082611b6b565b600581527f4e657875730000000000000000000000000000000000000000000000000000006020820152906040516139c8604082611b6b565b600581527f312e332e31000000000000000000000000000000000000000000000000000000602082015290565b9190916040818403126101e3578035613a0d81610265565b92602082013567ffffffffffffffff81116101e357611ba29201611d5b565b90613a42906001600160a01b03938101906139f5565b92166001600160a01b03821680158015613b97575b613b84576001600160a01b03613a8b835f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b541603613b68579181613b0c6134c494613af1613acc610db56134b1975f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b915f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b906001600160a01b03166001600160a01b0319825416179055565b613b47613b37825f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b6001600160a01b03198154169055565b6134bf5a92604051948591638a91b0e360e01b602084015260248301612104565b637c84ecfb60e01b5f526001600160a01b03821660045260245ffd5b50637c84ecfb60e01b5f5260045260245ffd5b5060018114613a57565b90613bb7906001600160a01b03938101906139f5565b92166001600160a01b03821680158015613c91575b613b84576001600160a01b03613c00835f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b541603613b68579181613c666134c494613af1613c41610db56134b1975f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b915f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b613b47613b37825f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b5060018114613bcc565b613ca6610585613154565b80613da4575b613cb257565b60015f525f516020615f635f395f51905f52602052613cf07ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7610db5565b6001600160a01b03811680151580613d99575b15613d885760405163d60b347f60e01b815230600482015290602090829060249082905afa9081156104ee575f91613d6a575b50613d4957613d4490614397565b613cf0565b6001600160a01b036001915b1614613d5d57565b63cc319d845f526004601cfd5b613d82915060203d811161201c5761200e8183611b6b565b5f613d36565b506001600160a01b03600191613d55565b506001811415613d03565b5060405163d60b347f60e01b81523060048201526020816024817f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b726001600160a01b03165afa9081156104ee575f91613dff575b5015613cac565b613e18915060203d60201161201c5761200e8183611b6b565b5f613df8565b6001600160a01b031680600114159081613e36575090565b90505f525f516020615f635f395f51905f526020526001600160a01b0360405f205416151590565b6001600160a01b031680600114159081613e76575090565b90505f525f516020615fa35f395f51905f526020526001600160a01b0360405f205416151590565b67ffffffffffffffff8111611b665760051b60200190565b60408051909190613ec78382611b6b565b6001815291601f1901825f5b828110613edf57505050565b806060602080938501015201613ed3565b90613efa82613e9e565b613f076040519182611b6b565b8281528092613f18601f1991613e9e565b01905f5b828110613f2857505050565b806060602080938501015201613f1c565b80511561358b5760200190565b805182101561358b5760209160051b010190565b91611ba29391613f72916040855260408501916120be565b91602081840391015261101d565b90613f8a9161542a565b916001600160f81b031981166140075750613fa482613ef0565b915f5b818110613fb45750505090565b80613feb613fc56001938587613569565b8035613fd081610265565b613fe36020830135926040810190611e85565b9290916155a9565b613ff58287613f46565b526140008186613f46565b5001613fa7565b9291906001600160f81b03198416600160f81b0361402a57611ba29293506154aa565b6001600160f81b0319846308c3ee0360e11b5f521660045260245ffd5b906140549193929361555a565b91909261405f613eb6565b956001600160f81b0319811661409157509061407b93916155a9565b61408483613f39565b5261408e82613f39565b50565b6001600160f81b03198116600160f81b036127e05750836140b3928492615582565b6140bc86613f39565b52156140c6575050565b7fb5282692b8c578af7fb880895d599035496b5e64d1f14bf428a1ed3bc406f6629161246261269e86613f39565b906140fe9161542a565b90916001600160f81b0319811661415b57505f5b81811061411e57505050565b8061415561412f6001938587613569565b803561413a81610265565b61414d6020830135926040810190611e85565b9290916155d6565b01614112565b92916001600160f81b03198416600160f81b0361402a5761408e9293506154aa565b6001600160f81b0319916141909161555a565b9094909391929116806141a957509061028193916155d6565b600160f81b81036141fe5750836141c1928492615582565b9290156141cd57505050565b6124627fb5282692b8c578af7fb880895d599035496b5e64d1f14bf428a1ed3bc406f6629360405193849384613f5a565b6308c3ee0360e11b5f5260045260245ffd5b600803614245576001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f06541690565b6001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f05541690565b6001600160a01b036142b781926001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b541691161490565b6142ef936001600160a01b0360209496939660405196879586948593637aa8f17760e11b85523360048601612c5f565b0392165afa9081156104ee575f91614378575b507f77390000000000000000000000000000000000000000000000000000000000007fffff0000000000000000000000000000000000000000000000000000000000008216148061435b575b614356575090565b905090565b506001600160e01b031982166001600160e01b031982161161434e565b614391915060203d602011611c8757611c788183611b6b565b5f614302565b6001600160a01b031680156143ca575f525f516020615f635f395f51905f526020526001600160a01b0360405f20541690565b637c84ecfb60e01b5f5260045260245ffd5b92909160a09492604051947ff6c866c1cd985ce61f030431e576c0e82887de0643dfa8a2e6efc3463e638ed08652602086015260408501526060840152818484019182372060808201522090565b92602092916001600160a01b03614443614461966144af565b60405196879586948593637aa8f17760e11b85523060048601612c5f565b0392165afa5f918161448e575b5061447857505f90565b6001600160e01b031916630b135d3f60e11b1490565b6144a891925060203d602011611c8757611c788183611b6b565b905f61446e565b7fd9a30f025a2615d2b3b61eed8380b0fd3193295a7bff5c2e5e0519b092f0ee7e7f00000000000000000000000000000000561dd60aea485cdb26e4618b1e40fd6e30147f00000000000000000000000000000000000000000000000000000000000b67d24614161561453a575b6719010000000000005f52601a52603a526042601820905f603a52565b5060a06040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527ff3fbaf4e62ef217b8151b366cdaba8fa578e78940637d6c1ec320d10a718877260208201527fd6f9477f1bbaaf55b72b2869a6c4697af6529f6af9f67d87058f1bf5bec0188760408201524660608201523060808201522061451d565b6001600160a01b0361028191166001600160a01b03166001600160a01b03195f516020615f835f395f51905f525416175f516020615f835f395f51905f5255565b92915f919082918261461381611d09565b966146216040519889611b6b565b818852601f1961463083611d09565b013660208a013760208451940192f13d80614653575b8084525f602085013e9190565b505f614646565b9061466482613e9e565b6146716040519182611b6b565b8281528092614682601f1991613e9e565b0190602036910137565b5f198114611a535760010190565b5f19810191908211611a5357565b604051906060820182811067ffffffffffffffff821117611b665760405260606040835f81525f60208201520152565b919081101561358b5760051b81013590607e19813603018212156101e3570190565b634e487b7160e01b5f52602160045260245ffd5b6003111561471857565b6146fa565b3560038110156101e35790565b805191908290602001825e015f815290565b610281906125476147589493604051958693602085019061472a565b9061472a565b908160209103126101e35751611ba281610265565b919081101561358b5760051b81013590603e19813603018212156101e3570190565b91905f5b8181106147a7575050505050565b6147b2818386614773565b90856147bd836159cd565b6147c6816159c3565b6147f8576147f2906147de8460206001960190611e85565b506040810135908760208201359135615e8e565b01614799565b50906001614805826159cd565b61480e816159c3565b0361489f57806020614821920190611e85565b50905f806148416040850135850160405191828260208294359101612360565b039060208601355afa61485261236d565b901561487757866148729184608060019601359160608201359135615e8e565b6147f2565b7f6e47f619000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fe2121978000000000000000000000000000000000000000000000000000000005f5260045ffd5b60015f525f516020615fa35f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13546001600160a01b03166149ad5760015f525f516020615fa35f395f51905f5260205261493a60405f2060016001600160a01b0319825416179055565b60015f525f516020615f635f395f51905f526020526001600160a01b037ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c754166149ad5760015f525f516020615f635f395f51905f5260205261028160405f2060016001600160a01b0319825416179055565b7f53c85e66000000000000000000000000000000000000000000000000000000005f5260045ffd5b919081101561358b5760051b0190565b9082101561358b576102ed9160051b810190611e85565b9150614a1e908035810191602083019235916020810135019060208201913590565b929391808403614afd575f5b818110614a3957505050505050565b80614a4760019284896149d5565b35828103614a6b5750614a65614a5e8288876149e5565b9087615260565b01614a2a565b60028103614a8e5750614a89614a828288876149e5565b9087615160565b614a65565b60038103614aac5750614a89614aa58288876149e5565b9087614e7c565b60048103614aca5750614a89614ac38288876149e5565b9087614d0f565b856002600719830110614adf575b5050614a65565b614af691614aee848a896149e5565b929091614b25565b5f85614ad8565b7fb4fa3fb3000000000000000000000000000000000000000000000000000000005f5260045ffd5b9291906001600160a01b03614b4e6001600160a01b035f516020615f835f395f51905f52541690565b1680614b5e575061028193614c3b565b916040939193519363d68f602560e01b85525f8580614b82363433600485016120de565b038183885af19485156104ee575f95614bde575b50614ba2939495614c3b565b803b156101e357614bcd5f92918392604051948580948193630b9dfbed60e11b835260048301612104565b03925af180156104ee576130005750565b614ba2949550614bf7903d805f833e6109bc8183611b6b565b9493614b96565b15614c065750565b6001600160a01b03907fc689cd97000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60405163ecd0596160e01b8152600481018290526001600160a01b038316949290602081602481895afa9081156104ee575f91614cf0575b5015614cc85781614c99614c89614c9e94614210565b6001600160a01b03811615614bfe565b61561f565b823b156101e357614bcd925f92836040518096819582946306d61fe760e41b845260048401613450565b7fe27c1dd8000000000000000000000000000000000000000000000000000000005f5260045ffd5b614d09915060203d60201161201c5761200e8183611b6b565b5f614c73565b91906001600160a01b03614d376001600160a01b035f516020615f835f395f51905f52541690565b1680614d47575061028192614de6565b90916040519263d68f602560e01b84525f8480614d69363433600485016120de565b038183875af19384156104ee575f94614d89575b50614ba2929394614de6565b614ba2939450614da2903d805f833e6109bc8183611b6b565b9392614d7d565b15614db15750565b6001600160a01b03907f741cbe03000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60405163ecd0596160e01b81526004808201526001600160a01b038216939190602081602481885afa9081156104ee575f91614e5d575b5015614cc857614c9e90614e58614e486001600160a01b035f516020615f835f395f51905f52541690565b6001600160a01b03811615614da9565b6145c1565b614e76915060203d60201161201c5761200e8183611b6b565b5f614e1d565b91906001600160a01b03614ea46001600160a01b035f516020615f835f395f51905f52541690565b1680614eb4575061028192614fc1565b90916040519263d68f602560e01b84525f8480614ed6363433600485016120de565b038183875af19384156104ee575f94614ef6575b50614ba2929394614fc1565b614ba2939450614f0f903d805f833e6109bc8183611b6b565b9392614eea565b906004101561358b5760040190565b15614f2c57565b7f867a1dcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b15614f5b57565b7fc001660b000000000000000000000000000000000000000000000000000000005f5260045ffd5b15614f8b5750565b6001600160e01b0319907fa56a04dd000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90916001600160a01b0382169160405163ecd0596160e01b815260208180614ff160048201906003602083019252565b0381875afa9081156104ee575f91615141575b5015614cc8576124bf8461060c6150766105218680615054615047615039836150336124b96150e59e8c611aae565b99614f16565b356001600160f81b03191690565b6001600160f81b03191690565b9a6001600160f81b03198c1615801561512a575b61507190614f25565b611ad9565b966150a56001600160e01b031984166306d61fe760e41b8114908115615119575b8115615110575b5015614f54565b6150ba836150b5610585826159da565b614f83565b6150d46150c5611cfa565b6001600160a01b039096168652565b6001600160f81b0319166020850152565b803b156101e357614bcd5f929183926040519485809481936306d61fe760e41b835260048301612104565b9050155f61509e565b638a91b0e360e01b81149150615097565b50607f60f91b6001600160f81b03198d1614615068565b61515a915060203d60201161201c5761200e8183611b6b565b5f615004565b91906001600160a01b036151886001600160a01b035f516020615f835f395f51905f52541690565b16806151985750610281926151fa565b90916040519263d68f602560e01b84525f84806151ba363433600485016120de565b038183875af19384156104ee575f946151da575b50614ba29293946151fa565b614ba29394506151f3903d805f833e6109bc8183611b6b565b93926151ce565b60405163ecd0596160e01b8152600260048201526001600160a01b038216939190602081602481885afa9081156104ee575f91615241575b5015614cc857614c9e90615a24565b61525a915060203d60201161201c5761200e8183611b6b565b5f615232565b91906001600160a01b036152886001600160a01b035f516020615f835f395f51905f52541690565b16806152985750610281926152fa565b90916040519263d68f602560e01b84525f84806152ba363433600485016120de565b038183875af19384156104ee575f946152da575b50614ba29293946152fa565b614ba29394506152f3903d805f833e6109bc8183611b6b565b93926152ce565b60405163ecd0596160e01b8152600160048201526001600160a01b038216939190602081602481885afa9081156104ee575f91615399575b5015614cc8576001600160a01b037f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b7216841461537157614c9e90615b48565b7fabc3af79000000000000000000000000000000000000000000000000000000005f5260045ffd5b6153b2915060203d60201161201c5761200e8183611b6b565b5f615332565b91816014116101e357823560601c92601401916013190190565b5f9192806040519485378338925af4913d82523d5f602084013e60203d830101604052565b5f919392806040519586378438925af415615421573d82523d5f602084013e60203d830101604052565b503d5f823e3d90fd5b909181359182810193601f199101016020840193803593828560051b8301119060401c1761549d578361545b575050565b835b5f190160208160051b8301013580830160608101908135809101918680602080860135809601011191111792171760401c1761549d578061545d57505050565b63ba597e7e5f526004601cfd5b9190916154b683613ef0565b925f5b8181106154c557505050565b806154d36001928486613569565b8035906154df82610265565b6154fe602082013560408301936154f68585611e85565b929091615582565b615508858b613f46565b5215615517575b5050016154b9565b7fb5282692b8c578af7fb880895d599035496b5e64d1f14bf428a1ed3bc406f6629161554291611e85565b9061555061269e858b613f46565b0390a15f8061550f565b90806014116101e357813560601c92603482106101e357601483013592603401916033190190565b905f928491604051958692833738935af1913d82523d5f602084013e60203d830101604052565b90925f92819594604051968792833738935af115615421573d82523d5f602084013e60203d830101604052565b8380935f93604051958692833738935af1156108925701604052565b60405190602036830101604052816014360181525f602036920137604051601481016040523360601b9052565b6008810361568f57506001600160a01b0361028191166001600160a01b03166001600160a01b03197f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f065416177f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0655565b6009146156995750565b6001600160a01b0361028191166001600160a01b03166001600160a01b03197f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f055416177f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0555565b1561570757565b6064604051635107885760e11b815260206004820152601860248201527f496e76616c696420706172616d44617461206c656e67746800000000000000006044820152fd5b6060906020810161575c8161471d565b6157658161470e565b6157a95750611ba291508060406105219201906157a46157858383611e85565b61579f6157956060860186613590565b9390923691611d25565b615d32565b611e85565b60016157b48261471d565b6157bd8161470e565b03615827575090505f806157d46040840184611e85565b50604051806157ee81602085810135860180359101612360565b0391355afa906157fc61236d565b911561581a57615813816060611ba2930190613590565b9083615d32565b636533cc8d5f526004601cfd5b61583260029161471d565b61583b8161470e565b0361594f57602861585a6158526040840184611e85565b909214615700565b6014810135831c9035831c806158c75750906158b3613358936158ad611ba29431935b604051926158a58461589788602083019190602083019252565b03601f198101865285611b6b565b810190613590565b91615d32565b604051928391602083019190602083019252565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03929092166004830152909190602090839060249082905afa80156104ee57613358936158ad6158b392611ba2955f91615930575b509361587d565b615949915060203d6020116104e7576104d98183611b6b565b5f615929565b604051635107885760e11b815260206004820152601a60248201527f496e76616c696420706172616d206665746368657220747970650000000000006044820152606490fd5b9291905f91604051943892602083519301915af115615421573d82523d5f602084013e60203d830101604052565b6002111561471857565b3560028110156101e35790565b615a1d6001600160a01b03916001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b5416151590565b6001600160a01b03811680158015615b3e575b6143ca575f9081525f516020615fa35f395f51905f5260205260409020546001600160a01b0316615b0a5760015f525f516020615fa35f395f51905f5260205261028190615acf615aa77fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13610db5565b613af1835f516020615fa35f395f51905f52906001600160a01b03165f5260205260405f2090565b60015f525f516020615fa35f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13613af1565b7f40d3d1a4000000000000000000000000000000000000000000000000000000005f526001600160a01b031660045260245ffd5b5060018114615a37565b6001600160a01b03811680158015615c2e575b6143ca575f9081525f516020615f635f395f51905f5260205260409020546001600160a01b0316615b0a5760015f525f516020615f635f395f51905f5260205261028190615bf3615bcb7ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7610db5565b613af1835f516020615f635f395f51905f52906001600160a01b03165f5260205260405f2090565b60015f525f516020615f635f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7613af1565b5060018114615b5b565b6040813603126101e35760405190615c4f82611b4a565b803560048110156101e357825260208101359067ffffffffffffffff82116101e357615c7d91369101611d5b565b602082015290565b6004111561471857565b91908260409103126101e3576020825192015190565b15615cac57565b630a31844b60e41b5f52600360045260245ffd5b15615cc757565b630a31844b60e41b5f52600260045260245ffd5b15615ce257565b630a31844b60e41b5f52600160045260245ffd5b15615cfd57565b630a31844b60e41b5f525f60045260245ffd5b602081519101519060208110615d24575090565b5f199060200360031b1b1690565b9180615d3d57505050565b5f5b818110615d4c5750505050565b615d5f615d5a828486614773565b615c38565b9060208160051b86010151918051615d7681615c85565b615d7f81615c85565b615da257600192615d966020615d9c930151615d10565b14615cf6565b01615d3f565b60018151615daf81615c85565b615db881615c85565b03615ddc57600192615dd06020615dd7930151615d10565b1115615cdb565b615d9c565b60028151615de981615c85565b615df281615c85565b03615e1157600192615e0a6020615dd7930151615d10565b1015615cc0565b60038151615e1e81615c85565b615e2781615c85565b03615e6657600192615e496020615dd793015160208082518301019101615c8f565b908210159182615e5b575b5050615ca5565b111590505f80615e54565b7f2c50c45e000000000000000000000000000000000000000000000000000000005f5260045ffd5b929193905f5b848110615ea357505050505050565b60208160051b83010151906001600160a01b038716916040516020810190615edb81613358868b869091604092825260208201520190565b519020833b156101e3576040517fa39e0787000000000000000000000000000000000000000000000000000000008152600481019190915260248101919091526001600160a01b0385166044820152915f908390606490829084905af19182156104ee57600192615f4e575b5001615e94565b8061099d5f615f5c93611b6b565b5f615f4756fe0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f000bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f030bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f01a164736f6c634300081b000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0320000000000000000000000000000000055c766a7060797fbc7be40c08b296b7200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000
-----Decoded View---------------
Arg [0] : anEntryPoint (address): 0x0000000071727De22E5E9d8BAf0edAc6f37da032
Arg [1] : defaultValidator (address): 0x0000000055C766a7060797FBc7Be40c08B296b72
Arg [2] : initData (bytes): 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032
Arg [1] : 0000000000000000000000000000000055c766a7060797fbc7be40c08b296b72
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000014
Arg [4] : eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.