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 | |||
|---|---|---|---|---|---|---|
| 16895275 | 2 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
NexusBootstrap
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. For security issues, contact: [email protected] import { ModuleManager } from "../base/ModuleManager.sol"; import { IModule } from "erc7579/interfaces/IERC7579Module.sol"; import { MODULE_TYPE_VALIDATOR, MODULE_TYPE_EXECUTOR, MODULE_TYPE_FALLBACK, MODULE_TYPE_HOOK } from "../../types/Constants.sol"; /// @title NexusBootstrap Configuration for Nexus /// @notice Provides configuration and initialization for Nexus smart accounts. /// @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 struct BootstrapConfig { address module; bytes data; } struct BootstrapPreValidationHookConfig { uint256 hookType; address module; bytes data; } /// @title NexusBootstrap /// @notice Manages the installation of modules into Nexus smart accounts using delegatecalls. contract NexusBootstrap is ModuleManager { constructor(address defaultValidator, bytes memory initData) ModuleManager(defaultValidator, initData) { } modifier _withInitSentinelLists() { _initSentinelLists(); _; } /// @notice Initializes the Nexus account with the default validator. /// @dev Intended to be called by the Nexus with a delegatecall. /// @param data The initialization data for the default validator module. function initNexusWithDefaultValidator(bytes calldata data) external payable { IModule(_DEFAULT_VALIDATOR).onInstall(data); } // ================================================ // ===== DEFAULT VALIDATOR + OTHER MODULES ===== // ================================================ /// @notice Initializes the Nexus account with the default validator and other modules. /// @dev Intended to be called by the Nexus with a delegatecall. /// @param defaultValidatorInitData The initialization data for the default validator module. /// @param validators The configuration array for validator modules. Should not contain the default validator. /// @param executors The configuration array for executor modules. /// @param hook The configuration for the hook module. /// @param fallbacks The configuration array for fallback handler modules. /// @param preValidationHooks The configuration array for pre-validation hooks. function initNexusWithDefaultValidatorAndOtherModules( bytes calldata defaultValidatorInitData, BootstrapConfig[] calldata validators, BootstrapConfig[] calldata executors, BootstrapConfig calldata hook, BootstrapConfig[] calldata fallbacks, BootstrapPreValidationHookConfig[] calldata preValidationHooks ) public payable _withInitSentinelLists { IModule(_DEFAULT_VALIDATOR).onInstall(defaultValidatorInitData); for (uint256 i; i < validators.length; i++) { if (validators[i].module == address(0)) continue; _installValidator(validators[i].module, validators[i].data); emit ModuleInstalled(MODULE_TYPE_VALIDATOR, validators[i].module); } for (uint256 i; i < executors.length; i++) { if (executors[i].module == address(0)) continue; _installExecutor(executors[i].module, executors[i].data); emit ModuleInstalled(MODULE_TYPE_EXECUTOR, executors[i].module); } // Initialize hook if (hook.module != address(0)) { _installHook(hook.module, hook.data); emit ModuleInstalled(MODULE_TYPE_HOOK, hook.module); } // Initialize fallback handlers for (uint256 i; i < fallbacks.length; i++) { if (fallbacks[i].module == address(0)) continue; _installFallbackHandler(fallbacks[i].module, fallbacks[i].data); emit ModuleInstalled(MODULE_TYPE_FALLBACK, fallbacks[i].module); } // Initialize pre-validation hooks for (uint256 i; i < preValidationHooks.length; i++) { if (preValidationHooks[i].module == address(0)) continue; _installPreValidationHook( preValidationHooks[i].hookType, preValidationHooks[i].module, preValidationHooks[i].data ); emit ModuleInstalled(preValidationHooks[i].hookType, preValidationHooks[i].module); } } /// @notice expose this function for backwards compatibility function initNexusWithDefaultValidatorAndOtherModulesNoRegistry( bytes calldata defaultValidatorInitData, BootstrapConfig[] calldata validators, BootstrapConfig[] calldata executors, BootstrapConfig calldata hook, BootstrapConfig[] calldata fallbacks, BootstrapPreValidationHookConfig[] calldata preValidationHooks ) external payable { initNexusWithDefaultValidatorAndOtherModules( defaultValidatorInitData, validators, executors, hook, fallbacks, preValidationHooks ); } // ================================================ // ===== SINGLE VALIDATOR ===== // ================================================ /// @notice Initializes the Nexus account with a single validator. /// @dev Intended to be called by the Nexus with a delegatecall. /// @param validator The address of the validator module. Should not be the default validator. /// @param data The initialization data for the validator module. function initNexusWithSingleValidator( address validator, bytes calldata data ) public payable _withInitSentinelLists { _installValidator(validator, data); emit ModuleInstalled(MODULE_TYPE_VALIDATOR, validator); } /// @notice expose this function for backwards compatibility function initNexusWithSingleValidatorNoRegistry(address validator, bytes calldata data) external payable { initNexusWithSingleValidator(validator, data); } // ================================================ // ===== GENERALIZED FLOW ===== // ================================================ /// @notice Initializes the Nexus account with multiple modules. /// @dev Intended to be called by the Nexus with a delegatecall. /// @param validators The configuration array for validator modules. Should not contain the default validator. /// @param executors The configuration array for executor modules. /// @param hook The configuration for the hook module. /// @param fallbacks The configuration array for fallback handler modules. /// @param preValidationHooks The configuration array for pre-validation hooks. function initNexus( BootstrapConfig[] calldata validators, BootstrapConfig[] calldata executors, BootstrapConfig calldata hook, BootstrapConfig[] calldata fallbacks, BootstrapPreValidationHookConfig[] calldata preValidationHooks ) public _withInitSentinelLists { // Initialize validators for (uint256 i = 0; i < validators.length; i++) { _installValidator(validators[i].module, validators[i].data); emit ModuleInstalled(MODULE_TYPE_VALIDATOR, validators[i].module); } // Initialize executors for (uint256 i = 0; i < executors.length; i++) { if (executors[i].module == address(0)) continue; _installExecutor(executors[i].module, executors[i].data); emit ModuleInstalled(MODULE_TYPE_EXECUTOR, executors[i].module); } // Initialize fallback handlers for (uint256 i = 0; i < fallbacks.length; i++) { if (fallbacks[i].module == address(0)) continue; _installFallbackHandler(fallbacks[i].module, fallbacks[i].data); emit ModuleInstalled(MODULE_TYPE_FALLBACK, fallbacks[i].module); } // Initialize hook if (hook.module != address(0)) { _installHook(hook.module, hook.data); emit ModuleInstalled(MODULE_TYPE_HOOK, hook.module); } // Initialize pre-validation hooks for (uint256 i = 0; i < preValidationHooks.length; i++) { if (preValidationHooks[i].module == address(0)) continue; _installPreValidationHook( preValidationHooks[i].hookType, preValidationHooks[i].module, preValidationHooks[i].data ); emit ModuleInstalled(preValidationHooks[i].hookType, preValidationHooks[i].module); } } /// @notice expose this function for backwards compatibility function initNexusNoRegistry( BootstrapConfig[] calldata validators, BootstrapConfig[] calldata executors, BootstrapConfig calldata hook, BootstrapConfig[] calldata fallbacks, BootstrapPreValidationHookConfig[] calldata preValidationHooks ) external payable { initNexus(validators, executors, hook, fallbacks, preValidationHooks); } // ================================================ // ===== SCOPED FLOW ===== // ================================================ /// @notice Initializes the Nexus account with a scoped set of modules. /// @dev Intended to be called by the Nexus with a delegatecall. /// @param validators The configuration array for validator modules. Should not contain the default validator. /// @param hook The configuration for the hook module. function initNexusScoped( BootstrapConfig[] calldata validators, BootstrapConfig calldata hook ) public _withInitSentinelLists { // Initialize validators for (uint256 i = 0; i < validators.length; i++) { _installValidator(validators[i].module, validators[i].data); emit ModuleInstalled(MODULE_TYPE_VALIDATOR, validators[i].module); } // Initialize hook if (hook.module != address(0)) { _installHook(hook.module, hook.data); emit ModuleInstalled(MODULE_TYPE_HOOK, hook.module); } } /// @notice expose this function for backwards compatibility function initNexusScopedNoRegistry( BootstrapConfig[] calldata validators, BootstrapConfig calldata hook ) external payable { initNexusScoped(validators, hook); } /// @dev EIP712 domain name and version. function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) { name = "NexusBootstrap"; version = "1.3.0"; } // required implementations. Are not used. function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external payable override { // do nothing } function uninstallModule( uint256 moduleTypeId, address module, bytes calldata deInitData ) external payable override { // do nothing } // solhint-disable no-unused-vars function isModuleInstalled( uint256 moduleTypeId, address module, bytes calldata additionalContext ) external view override returns (bool installed) { return false; } // solhint-enable no-unused-vars }
// 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.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.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] 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;
/// @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: 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;
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: 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] /// @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.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.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 { 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);
}{
"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":"defaultValidator","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DefaultValidatorAlreadyInstalled","type":"error"},{"inputs":[],"name":"EmergencyUninstallSigError","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":"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":"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":[{"internalType":"address","name":"currentPreValidationHook","type":"address"}],"name":"PrevalidationHookAlreadyInstalled","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"UnauthorizedOperation","type":"error"},{"inputs":[{"internalType":"CallType","name":"callType","type":"bytes1"}],"name":"UnsupportedCallType","type":"error"},{"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"},{"stateMutability":"payable","type":"fallback"},{"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":[],"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":[{"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":[{"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":[{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"validators","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"executors","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig","name":"hook","type":"tuple"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"fallbacks","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"hookType","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapPreValidationHookConfig[]","name":"preValidationHooks","type":"tuple[]"}],"name":"initNexus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"validators","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"executors","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig","name":"hook","type":"tuple"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"fallbacks","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"hookType","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapPreValidationHookConfig[]","name":"preValidationHooks","type":"tuple[]"}],"name":"initNexusNoRegistry","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"validators","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig","name":"hook","type":"tuple"}],"name":"initNexusScoped","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"validators","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig","name":"hook","type":"tuple"}],"name":"initNexusScopedNoRegistry","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"initNexusWithDefaultValidator","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"defaultValidatorInitData","type":"bytes"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"validators","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"executors","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig","name":"hook","type":"tuple"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"fallbacks","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"hookType","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapPreValidationHookConfig[]","name":"preValidationHooks","type":"tuple[]"}],"name":"initNexusWithDefaultValidatorAndOtherModules","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"defaultValidatorInitData","type":"bytes"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"validators","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"executors","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig","name":"hook","type":"tuple"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig[]","name":"fallbacks","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"hookType","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapPreValidationHookConfig[]","name":"preValidationHooks","type":"tuple[]"}],"name":"initNexusWithDefaultValidatorAndOtherModulesNoRegistry","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"validator","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"initNexusWithSingleValidator","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"validator","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"initNexusWithSingleValidatorNoRegistry","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":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"additionalContext","type":"bytes"}],"name":"isModuleInstalled","outputs":[{"internalType":"bool","name":"installed","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"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
610140806040523461023e57612c57803803809161001d82856102aa565b833981019060408183031261023e578051906001600160a01b0382169081830361023e576020810151906001600160401b03821161023e570183601f8201121561023e5780516001600160401b0381116102965760405191610089601f8301601f1916602001846102aa565b81835260208301956020838301011161023e57815f926020809301885e83010152306080524660a05260409360a085516100c387826102aa565b600e815260208101906d04e65787573426f6f7473747261760941b82528751916100ed89846102aa565b600583526020830191640312e332e360dc1b8352519020915190208160c0528060e0528751917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f83526020830152878201524660608201523060808201522061010052845163ecd0596160e01b815260016004820152602081602481875afa90811561028c575f91610251575b501561024257823b1561023e576044925f928387519586809581946306d61fe760e41b8352602060048401525180918160248501528484015e8181018301849052601f01601f191681010301925af1801561023457610224575b50610120525161298990816102ce823960805181505060a05181505060c05181505060e05181505061010051815050610120518181816101920152818161053a015281816108d101526119c30152f35b5f61022e916102aa565b5f6101d4565b83513d5f823e3d90fd5b5f80fd5b631c4f83bb60e31b5f5260045ffd5b90506020813d602011610284575b8161026c602093836102aa565b8101031261023e5751801515810361023e575f61017a565b3d915061025f565b86513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b038211908210176102965760405256fe60806040526004361015610015575b3661158d57005b5f3560e01c80630a664dba146101255780630b3dc3541461012057806310b7fca9146100f8578063112d3a7d1461011b578063220911871461010c578063310259841461011657806334ea61a51461011157806341bede031461010c578063481ddd231461010757806354ed06d5146101025780635faac46b146100fd5780636d583e36146100f857806384b0196e146100f357806386437876146100ee5780639517e29f146100e9578063a71763a8146100e9578063a8a33cf2146100e45763ea5f61d00361000e57611374565b61117c565b611173565b610f80565b610daf565b610232565b610bc8565b610aff565b610a41565b61050b565b610994565b61089a565b6103d8565b610173565b610138565b5f91031261013457565b5f80fd5b34610134575f3660031901126101345760206001600160a01b035f51602061293d5f395f51905f5254166001600160a01b0360405191168152f35b34610134575f3660031901126101345760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b6001600160a01b0381160361013457565b9181601f840112156101345782359167ffffffffffffffff8311610134576020838186019501011161013457565b9060406003198301126101345760043561020e816101b6565b916024359067ffffffffffffffff82116101345761022e916004016101c7565b9091565b61023b366101f5565b6102469291926122f9565b61024e612394565b6001600160a01b036102746001600160a01b035f51602061293d5f395f51905f52541690565b16806102be5750906102986102b9925f5160206128fd5f395f51905f52948361197b565b60408051600181526001600160a01b03909216602083015290918291820190565b0390a1005b906040519363d68f602560e01b85525f85806102df36343360048501611848565b038183875af194851561036d575f95610372575b50906102ff918461197b565b803b156101345761032a5f93918492604051958680948193630b9dfbed60e11b83526004830161186e565b03925af190811561036d575f5160206128fd5f395f51905f52926102b992610353575b50610298565b806103615f61036793611484565b8061012a565b5f61034d565b6114d7565b6102ff92919550610394903d805f833e61038c8183611484565b8101906117e5565b9490916102f3565b606060031982011261013457600435916024356103b8816101b6565b916044359067ffffffffffffffff82116101345761022e916004016101c7565b34610134576103e63661039c565b5050505060206040515f8152f35b9181601f840112156101345782359167ffffffffffffffff8311610134576020808501948460051b01011161013457565b908160409103126101345790565b9060c06003198301126101345760043567ffffffffffffffff8111610134578261045f916004016101c7565b9290929160243567ffffffffffffffff81116101345782610482916004016103f4565b9290929160443567ffffffffffffffff811161013457826104a5916004016103f4565b9290929160643567ffffffffffffffff811161013457826104c891600401610425565b9160843567ffffffffffffffff811161013457816104e8916004016103f4565b9290929160a4359067ffffffffffffffff82116101345761022e916004016103f4565b61051436610433565b9890996105289892989793979694966122f9565b610530612394565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691823b1561013457610585925f92836040518096819582946306d61fe760e41b8452600484016114c6565b03925af1801561036d57610886575b505f5b82811061081a575050505f5b82811061078d575050506105c56105b98261151d565b6001600160a01b031690565b61072f575b505f5b828110610693575050505f5b8181106105e257005b806105fe6105b960206105f8600195878961155a565b0161151d565b1561068e5761064361061182858761155a565b3561062260206105f885888a61155a565b9061063b61063185888a61155a565b6040810190611527565b92909161216e565b5f5160206128fd5f395f51905f5261065c82858761155a565b3561066d60206105f885888a61155a565b604080519283526001600160a01b0391909116602083015290a15b016105d9565b610688565b806106ac6105b96106a760019487876114f6565b61151d565b1561072a576106e06106c26106a78387876114f6565b6106da6106d08488886114f6565b6020810190611527565b91611d37565b5f5160206128fd5f395f51905f526107206106ff6106a78488886114f6565b60408051600381526001600160a01b03909216602083015290918291820190565b0390a15b016105cd565b610724565b610784610763826106a76107505f5160206128fd5f395f51905f529561151d565b61075d6020840184611527565b91611b9b565b60408051600481526001600160a01b03909216602083015290918291820190565b0390a15f6105ca565b806107a16105b96106a760019487876114f6565b15610815576107cb6107b76106a78387876114f6565b6107c56106d08488886114f6565b91611a9b565b5f5160206128fd5f395f51905f5261080b6107ea6106a78488886114f6565b60408051600281526001600160a01b03909216602083015290918291820190565b0390a15b016105a3565b61080f565b8061082e6105b96106a760019487876114f6565b15610881576108586108446106a78387876114f6565b6108526106d08488886114f6565b9161187f565b5f5160206128fd5f395f51905f526108776102986106a78488886114f6565b0390a15b01610597565b61087b565b806103615f61089493611484565b5f610594565b5f60203660031901126101345760043567ffffffffffffffff8111610134576108c79036906004016101c7565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691823b1561013457610923925f92836040518096819582946306d61fe760e41b845260206004850181815201916114a6565b03925af1801561036d57610935575080f35b61094191505f90611484565b005b60406003198201126101345760043567ffffffffffffffff8111610134578161096e916004016103f4565b929092916024359067ffffffffffffffff82116101345761099191600401610425565b90565b34610134576109a236610943565b91906109ac6122f9565b6109b4612394565b5f5b8181106109f757836001600160a01b036109cf8261151d565b166109d657005b6102b9610763826106a76107505f5160206128fd5f395f51905f529561151d565b80610a19610a0b6106a760019486886114f6565b6108526106d08487896114f6565b5f5160206128fd5f395f51905f52610a386102986106a78487896114f6565b0390a1016109b6565b34610134576020366003190112610134576004356001600160e01b03198116810361013457610aa1906001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b60405190604082019082821067ffffffffffffffff831117610afa576040918252546001600160a01b03811680845260589190911b6001600160f81b0319166020938401819052825190815292830152819081015b0390f35b611470565b610b0836610943565b9190610b126122f9565b610b1a612394565b5f5b818110610b3557836001600160a01b036109cf8261151d565b80610b49610a0b6106a760019486886114f6565b5f5160206128fd5f395f51905f52610b686102986106a78487896114f6565b0390a101610b1c565b90929192604082016040835281518091526020606084019201905f5b818110610ba9575050506001600160a01b036020919416910152565b82516001600160a01b0316845260209384019390920191600101610b8d565b3461013457604036600319011261013457600435610be5816101b6565b6024359060016001600160a01b038216141580610d75575b610d5a578115610d325790610c11816127d4565b610c49610c3c5f946001600160a01b03165f525f51602061291d5f395f51905f5260205260405f2090565b546001600160a01b031690565b6001600160a01b0381168015159081610d26575b5080610d1d575b15610cbe57610cb2610c3c82610c8f610cb894610c818988612806565b906001600160a01b03169052565b6001600160a01b03165f525f51602061291d5f395f51905f5260205260405f2090565b9361282e565b92610c49565b908360016001600160a01b038416141580610d14575b610cea575b8152610af660405192839283610b71565b9150610d0e610d01610cfb84612841565b83612806565b516001600160a01b031690565b91610cd9565b50801515610cd4565b50828410610c64565b6001915014155f610c5d565b7ff7250817000000000000000000000000000000000000000000000000000000005f5260045ffd5b637c84ecfb60e01b5f526001600160a01b031660045260245ffd5b50610d86610d828261287c565b1590565b610bfd565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b34610134575f36600319011261013457610e6d6040610e7a815191610dd48184611484565b600e83527f4e65787573426f6f74737472617000000000000000000000000000000000000060208401528051610e0a8282611484565b600581527f312e332e30000000000000000000000000000000000000000000000000000000602082015281519485947f0f00000000000000000000000000000000000000000000000000000000000000865260e0602087015260e0860190610d8b565b9184830390850152610d8b565b4660608301523060808301525f60a083015281810360c083015260206060519182815201906080905f5b818110610eb2575050500390f35b8251845285945060209384019390920191600101610ea4565b9060a06003198301126101345760043567ffffffffffffffff81116101345782610ef7916004016103f4565b9290929160243567ffffffffffffffff81116101345782610f1a916004016103f4565b9290929160443567ffffffffffffffff81116101345782610f3d91600401610425565b9160643567ffffffffffffffff81116101345781610f5d916004016103f4565b929092916084359067ffffffffffffffff82116101345761022e916004016103f4565b610f8936610ecb565b96610f9b9891989692959394966122f9565b610fa3612394565b5f5b818110611137575050505f5b8281106110df575050505f5b82811061108757505050610fd36105b98261151d565b61105d575b505f5b818110610fe457005b80610ffa6105b960206105f8600195878961155a565b156110585761100d61061182858761155a565b5f5160206128fd5f395f51905f5261102682858761155a565b3561103760206105f885888a61155a565b604080519283526001600160a01b0391909116602083015290a15b01610fdb565b611052565b61107e610763826106a76107505f5160206128fd5f395f51905f529561151d565b0390a15f610fd8565b8061109b6105b96106a760019487876114f6565b156110da576110b16106c26106a78387876114f6565b5f5160206128fd5f395f51905f526110d06106ff6106a78488886114f6565b0390a15b01610fbd565b6110d4565b806110f36105b96106a760019487876114f6565b15611132576111096107b76106a78387876114f6565b5f5160206128fd5f395f51905f526111286107ea6106a78488886114f6565b0390a15b01610fb1565b61112c565b8061114b610a0b6106a760019486886114f6565b5f5160206128fd5f395f51905f5261116a6102986106a78487896114f6565b0390a101610fa5565b6109413661039c565b346101345761118a36610ecb565b9661119c9891989692959394966122f9565b6111a4612394565b5f5b818110611338575050505f5b8281106112e0575050505f5b828110611288575050506111d46105b98261151d565b61125e575b505f5b8181106111e557005b806111fb6105b960206105f8600195878961155a565b156112595761120e61061182858761155a565b5f5160206128fd5f395f51905f5261122782858761155a565b3561123860206105f885888a61155a565b604080519283526001600160a01b0391909116602083015290a15b016111dc565b611253565b61127f610763826106a76107505f5160206128fd5f395f51905f529561151d565b0390a15f6111d9565b8061129c6105b96106a760019487876114f6565b156112db576112b26106c26106a78387876114f6565b5f5160206128fd5f395f51905f526112d16106ff6106a78488886114f6565b0390a15b016111be565b6112d5565b806112f46105b96106a760019487876114f6565b156113335761130a6107b76106a78387876114f6565b5f5160206128fd5f395f51905f526113296107ea6106a78488886114f6565b0390a15b016111b2565b61132d565b8061134c610a0b6106a760019486886114f6565b5f5160206128fd5f395f51905f5261136b6102986106a78487896114f6565b0390a1016111a6565b3461013457604036600319011261013457600435611391816101b6565b6024359060016001600160a01b03821614158061145e575b610d5a578115610d3257906113bd816127d4565b6113e8610c3c5f946001600160a01b03165f525f51602061295d5f395f51905f5260205260405f2090565b6001600160a01b0381168015159081611452575b5080611449575b15610cbe57610cb2610c3c8261142061144394610c818988612806565b6001600160a01b03165f525f51602061295d5f395f51905f5260205260405f2090565b926113e8565b50828410611403565b6001915014155f6113fc565b5061146b610d82826128bc565b6113a9565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff821117610afa57604052565b908060209392818452848401375f828201840152601f01601f1916010190565b9160206109919381815201916114a6565b6040513d5f823e3d90fd5b634e487b7160e01b5f52603260045260245ffd5b91908110156115185760051b81013590603e1981360301821215610134570190565b6114e2565b35610991816101b6565b903590601e1981360301821215610134570180359067ffffffffffffffff82116101345760200191813603831361013457565b91908110156115185760051b81013590605e1981360301821215610134570190565b6040519061158b604083611484565b565b5f356001600160e01b03198116906115d6826001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b546115f56001600160a01b0382169160581b6001600160f81b03191690565b906001600160a01b03811661163e57505060e01c63bc197c81811463f23a6e6182141763150b7a0282141761163557506308c63e275f526020526024601cfd5b6020526020603cf35b5f51602061293d5f395f51905f52546001600160a01b0316801515926060929084611773575b6001600160f81b03198116607f60f91b0361170857505f809161168561284f565b90602082519201905afa926116986122ca565b935b15611700576116ac575b825160208401f35b803b15610134576116d75f92918392604051948580948193630b9dfbed60e11b83526004830161186e565b03925af1801561036d576116ec575b806116a4565b806103615f6116fa93611484565b816116e6565b835160208501fd5b6001600160f81b0319811661173e57505f809161172361284f565b906020825192019034905af1926117386122ca565b9361169a565b7fb96fcfe4000000000000000000000000000000000000000000000000000000005f526001600160f81b03191660045260245ffd5b925060405163d68f602560e01b81525f818061179436343360048501611848565b038183875af190811561036d575f916117af575b5092611664565b6117c391503d805f833e61038c8183611484565b856117a8565b67ffffffffffffffff8111610afa57601f01601f191660200190565b6020818303126101345780519067ffffffffffffffff8211610134570181601f8201121561013457805190611819826117c9565b926118276040519485611484565b8284526020838301011161013457815f9260208093018386015e8301015290565b61099193926001600160a01b03606093168252602082015281604082015201905f6114a6565b906020610991928181520190610d8b565b91906001600160a01b036118a76001600160a01b035f51602061293d5f395f51905f52541690565b16806118b7575061158b9261197b565b90916040519263d68f602560e01b84525f84806118d936343360048501611848565b038183875af193841561036d575f94611943575b506118f992939461197b565b803b15610134576119245f92918392604051948580948193630b9dfbed60e11b83526004830161186e565b03925af1801561036d576119355750565b806103615f61158b93611484565b6118f993945061195c903d805f833e61038c8183611484565b93926118ed565b90816020910312610134575180151581036101345790565b60405163ecd0596160e01b8152600160048201526001600160a01b038216939190602081602481885afa90811561036d575f91611a6c575b5015611a44576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168414611a1c576119f290612407565b823b1561013457611924925f92836040518096819582946306d61fe760e41b8452600484016114c6565b7fabc3af79000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fe27c1dd8000000000000000000000000000000000000000000000000000000005f5260045ffd5b611a8e915060203d602011611a94575b611a868183611484565b810190611963565b5f6119b3565b503d611a7c565b91906001600160a01b03611ac36001600160a01b035f51602061293d5f395f51905f52541690565b1680611ad3575061158b92611b35565b90916040519263d68f602560e01b84525f8480611af536343360048501611848565b038183875af193841561036d575f94611b15575b506118f9929394611b35565b6118f9939450611b2e903d805f833e61038c8183611484565b9392611b09565b60405163ecd0596160e01b8152600260048201526001600160a01b038216939190602081602481885afa90811561036d575f91611b7c575b5015611a44576119f290612558565b611b95915060203d602011611a9457611a868183611484565b5f611b6d565b91906001600160a01b03611bc36001600160a01b035f51602061293d5f395f51905f52541690565b1680611bd3575061158b92611c72565b90916040519263d68f602560e01b84525f8480611bf536343360048501611848565b038183875af193841561036d575f94611c15575b506118f9929394611c72565b6118f9939450611c2e903d805f833e61038c8183611484565b9392611c09565b15611c3d5750565b6001600160a01b03907f741cbe03000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60405163ecd0596160e01b81526004808201526001600160a01b038216939190602081602481885afa90811561036d575f91611d18575b5015611a44576119f290611ce4611cd46001600160a01b035f51602061293d5f395f51905f52541690565b6001600160a01b03811615611c35565b6001600160a01b03166001600160a01b03195f51602061293d5f395f51905f525416175f51602061293d5f395f51905f5255565b611d31915060203d602011611a9457611a868183611484565b5f611ca9565b91906001600160a01b03611d5f6001600160a01b035f51602061293d5f395f51905f52541690565b1680611d6f575061158b92611f13565b90916040519263d68f602560e01b84525f8480611d9136343360048501611848565b038183875af193841561036d575f94611db1575b506118f9929394611f13565b6118f9939450611dca903d805f833e61038c8183611484565b9392611da5565b906004116101345790600490565b909291928360051161013457831161013457600501916004190190565b919091356001600160e01b031981169260048110611e18575050565b6001600160e01b0319929350829060040360031b1b161690565b90600410156115185760040190565b15611e4857565b7f867a1dcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b929192611e7c826117c9565b91611e8a6040519384611484565b829481845281830111610134578281602093845f960137010152565b15611ead57565b7fc001660b000000000000000000000000000000000000000000000000000000005f5260045ffd5b15611edd5750565b6001600160e01b0319907fa56a04dd000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90916001600160a01b0382169160405163ecd0596160e01b815260208180611f4360048201906003602083019252565b0381875afa90811561036d575f9161214f575b5015611a445761207b84612044611fd5611fce8680611fac611f9f611f9183611f8b611f856120da9e8c611dd1565b90611dfc565b99611e32565b356001600160f81b03191690565b6001600160f81b03191690565b9a6001600160f81b03198c16158015612138575b611fc990611e41565b611ddf565b3691611e70565b966120046001600160e01b031984166306d61fe760e41b811490811561210e575b8115612105575b5015611ea6565b61201983612014610d8282612648565b611ed5565b61203361202461157c565b6001600160a01b039096168652565b6001600160f81b0319166020850152565b6001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b8151815460209093015174ff000000000000000000000000000000000000000060589190911c167fffffffffffffffffffffff0000000000000000000000000000000000000000009093166001600160a01b0390911617919091179055565b803b15610134576119245f929183926040519485809481936306d61fe760e41b83526004830161186e565b9050155f611ffd565b7f8a91b0e30000000000000000000000000000000000000000000000000000000081149150611ff6565b50607f60f91b6001600160f81b03198d1614611fc0565b612168915060203d602011611a9457611a868183611484565b5f611f56565b9291906001600160a01b036121976001600160a01b035f51602061293d5f395f51905f52541690565b16806121a7575061158b93612248565b916040939193519363d68f602560e01b85525f85806121cb36343360048501611848565b038183885af194851561036d575f956121eb575b506118f9939495612248565b6118f9949550612204903d805f833e61038c8183611484565b94936121df565b156122135750565b6001600160a01b03907fc689cd97000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60405163ecd0596160e01b8152600481018290526001600160a01b038316949290602081602481895afa90811561036d575f916122ab575b5015611a4457816122a66122966119f294612692565b6001600160a01b0381161561220b565b6126f5565b6122c4915060203d602011611a9457611a868183611484565b5f612280565b3d156122f4573d906122db826117c9565b916122e96040519384611484565b82523d5f602084013e565b606090565b60015f525f51602061295d5f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13546001600160a01b031661236c5760015f525f51602061295d5f395f51905f5260205261158b60405f2060016001600160a01b0319825416179055565b7f53c85e66000000000000000000000000000000000000000000000000000000005f5260045ffd5b60015f525f51602061291d5f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7546001600160a01b031661236c5760015f525f51602061291d5f395f51905f5260205261158b60405f2060016001600160a01b0319825416179055565b6001600160a01b0381168015801561254e575b61253c575f9081525f51602061291d5f395f51905f5260205260409020546001600160a01b03166125085760015f525f51602061291d5f395f51905f5260205261158b906124cd61248a7ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7610c3c565b6124b2835f51602061291d5f395f51905f52906001600160a01b03165f5260205260405f2090565b906001600160a01b03166001600160a01b0319825416179055565b60015f525f51602061291d5f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c76124b2565b7f40d3d1a4000000000000000000000000000000000000000000000000000000005f526001600160a01b031660045260245ffd5b637c84ecfb60e01b5f5260045260245ffd5b506001811461241a565b6001600160a01b0381168015801561263e575b61253c575f9081525f51602061295d5f395f51905f5260205260409020546001600160a01b03166125085760015f525f51602061295d5f395f51905f5260205261158b906126036125db7fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13610c3c565b6124b2835f51602061295d5f395f51905f52906001600160a01b03165f5260205260405f2090565b60015f525f51602061295d5f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a136124b2565b506001811461256b565b61268b6001600160a01b03916001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b5416151590565b6008036126c7576001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f06541690565b6001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f05541690565b6008810361275857506001600160a01b03166001600160a01b03197f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f065416177f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0655565b6009146127625750565b6001600160a01b03166001600160a01b03197f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f055416177f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0555565b67ffffffffffffffff8111610afa5760051b60200190565b906127de826127bc565b6127eb6040519182611484565b82815280926127fc601f19916127bc565b0190602036910137565b80518210156115185760209160051b010190565b634e487b7160e01b5f52601160045260245ffd5b5f19811461283c5760010190565b61281a565b5f1981019190821161283c57565b60405190602036830101604052816014360181525f602036920137604051601481016040523360601b9052565b6001600160a01b031680600114159081612894575090565b90505f525f51602061291d5f395f51905f526020526001600160a01b0360405f205416151590565b6001600160a01b0316806001141590816128d4575090565b90505f525f51602061295d5f395f51905f526020526001600160a01b0360405f20541615159056fed21d0b289f126c4b473ea641963e766833c2f13866e4ff480abd787c100ef1230bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f000bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f030bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f01a164736f6c634300081b000a0000000000000000000000000000000055c766a7060797fbc7be40c08b296b7200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000
Deployed Bytecode
0x60806040526004361015610015575b3661158d57005b5f3560e01c80630a664dba146101255780630b3dc3541461012057806310b7fca9146100f8578063112d3a7d1461011b578063220911871461010c578063310259841461011657806334ea61a51461011157806341bede031461010c578063481ddd231461010757806354ed06d5146101025780635faac46b146100fd5780636d583e36146100f857806384b0196e146100f357806386437876146100ee5780639517e29f146100e9578063a71763a8146100e9578063a8a33cf2146100e45763ea5f61d00361000e57611374565b61117c565b611173565b610f80565b610daf565b610232565b610bc8565b610aff565b610a41565b61050b565b610994565b61089a565b6103d8565b610173565b610138565b5f91031261013457565b5f80fd5b34610134575f3660031901126101345760206001600160a01b035f51602061293d5f395f51905f5254166001600160a01b0360405191168152f35b34610134575f3660031901126101345760206040516001600160a01b037f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b72168152f35b6001600160a01b0381160361013457565b9181601f840112156101345782359167ffffffffffffffff8311610134576020838186019501011161013457565b9060406003198301126101345760043561020e816101b6565b916024359067ffffffffffffffff82116101345761022e916004016101c7565b9091565b61023b366101f5565b6102469291926122f9565b61024e612394565b6001600160a01b036102746001600160a01b035f51602061293d5f395f51905f52541690565b16806102be5750906102986102b9925f5160206128fd5f395f51905f52948361197b565b60408051600181526001600160a01b03909216602083015290918291820190565b0390a1005b906040519363d68f602560e01b85525f85806102df36343360048501611848565b038183875af194851561036d575f95610372575b50906102ff918461197b565b803b156101345761032a5f93918492604051958680948193630b9dfbed60e11b83526004830161186e565b03925af190811561036d575f5160206128fd5f395f51905f52926102b992610353575b50610298565b806103615f61036793611484565b8061012a565b5f61034d565b6114d7565b6102ff92919550610394903d805f833e61038c8183611484565b8101906117e5565b9490916102f3565b606060031982011261013457600435916024356103b8816101b6565b916044359067ffffffffffffffff82116101345761022e916004016101c7565b34610134576103e63661039c565b5050505060206040515f8152f35b9181601f840112156101345782359167ffffffffffffffff8311610134576020808501948460051b01011161013457565b908160409103126101345790565b9060c06003198301126101345760043567ffffffffffffffff8111610134578261045f916004016101c7565b9290929160243567ffffffffffffffff81116101345782610482916004016103f4565b9290929160443567ffffffffffffffff811161013457826104a5916004016103f4565b9290929160643567ffffffffffffffff811161013457826104c891600401610425565b9160843567ffffffffffffffff811161013457816104e8916004016103f4565b9290929160a4359067ffffffffffffffff82116101345761022e916004016103f4565b61051436610433565b9890996105289892989793979694966122f9565b610530612394565b6001600160a01b037f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b721691823b1561013457610585925f92836040518096819582946306d61fe760e41b8452600484016114c6565b03925af1801561036d57610886575b505f5b82811061081a575050505f5b82811061078d575050506105c56105b98261151d565b6001600160a01b031690565b61072f575b505f5b828110610693575050505f5b8181106105e257005b806105fe6105b960206105f8600195878961155a565b0161151d565b1561068e5761064361061182858761155a565b3561062260206105f885888a61155a565b9061063b61063185888a61155a565b6040810190611527565b92909161216e565b5f5160206128fd5f395f51905f5261065c82858761155a565b3561066d60206105f885888a61155a565b604080519283526001600160a01b0391909116602083015290a15b016105d9565b610688565b806106ac6105b96106a760019487876114f6565b61151d565b1561072a576106e06106c26106a78387876114f6565b6106da6106d08488886114f6565b6020810190611527565b91611d37565b5f5160206128fd5f395f51905f526107206106ff6106a78488886114f6565b60408051600381526001600160a01b03909216602083015290918291820190565b0390a15b016105cd565b610724565b610784610763826106a76107505f5160206128fd5f395f51905f529561151d565b61075d6020840184611527565b91611b9b565b60408051600481526001600160a01b03909216602083015290918291820190565b0390a15f6105ca565b806107a16105b96106a760019487876114f6565b15610815576107cb6107b76106a78387876114f6565b6107c56106d08488886114f6565b91611a9b565b5f5160206128fd5f395f51905f5261080b6107ea6106a78488886114f6565b60408051600281526001600160a01b03909216602083015290918291820190565b0390a15b016105a3565b61080f565b8061082e6105b96106a760019487876114f6565b15610881576108586108446106a78387876114f6565b6108526106d08488886114f6565b9161187f565b5f5160206128fd5f395f51905f526108776102986106a78488886114f6565b0390a15b01610597565b61087b565b806103615f61089493611484565b5f610594565b5f60203660031901126101345760043567ffffffffffffffff8111610134576108c79036906004016101c7565b6001600160a01b037f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b721691823b1561013457610923925f92836040518096819582946306d61fe760e41b845260206004850181815201916114a6565b03925af1801561036d57610935575080f35b61094191505f90611484565b005b60406003198201126101345760043567ffffffffffffffff8111610134578161096e916004016103f4565b929092916024359067ffffffffffffffff82116101345761099191600401610425565b90565b34610134576109a236610943565b91906109ac6122f9565b6109b4612394565b5f5b8181106109f757836001600160a01b036109cf8261151d565b166109d657005b6102b9610763826106a76107505f5160206128fd5f395f51905f529561151d565b80610a19610a0b6106a760019486886114f6565b6108526106d08487896114f6565b5f5160206128fd5f395f51905f52610a386102986106a78487896114f6565b0390a1016109b6565b34610134576020366003190112610134576004356001600160e01b03198116810361013457610aa1906001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b60405190604082019082821067ffffffffffffffff831117610afa576040918252546001600160a01b03811680845260589190911b6001600160f81b0319166020938401819052825190815292830152819081015b0390f35b611470565b610b0836610943565b9190610b126122f9565b610b1a612394565b5f5b818110610b3557836001600160a01b036109cf8261151d565b80610b49610a0b6106a760019486886114f6565b5f5160206128fd5f395f51905f52610b686102986106a78487896114f6565b0390a101610b1c565b90929192604082016040835281518091526020606084019201905f5b818110610ba9575050506001600160a01b036020919416910152565b82516001600160a01b0316845260209384019390920191600101610b8d565b3461013457604036600319011261013457600435610be5816101b6565b6024359060016001600160a01b038216141580610d75575b610d5a578115610d325790610c11816127d4565b610c49610c3c5f946001600160a01b03165f525f51602061291d5f395f51905f5260205260405f2090565b546001600160a01b031690565b6001600160a01b0381168015159081610d26575b5080610d1d575b15610cbe57610cb2610c3c82610c8f610cb894610c818988612806565b906001600160a01b03169052565b6001600160a01b03165f525f51602061291d5f395f51905f5260205260405f2090565b9361282e565b92610c49565b908360016001600160a01b038416141580610d14575b610cea575b8152610af660405192839283610b71565b9150610d0e610d01610cfb84612841565b83612806565b516001600160a01b031690565b91610cd9565b50801515610cd4565b50828410610c64565b6001915014155f610c5d565b7ff7250817000000000000000000000000000000000000000000000000000000005f5260045ffd5b637c84ecfb60e01b5f526001600160a01b031660045260245ffd5b50610d86610d828261287c565b1590565b610bfd565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b34610134575f36600319011261013457610e6d6040610e7a815191610dd48184611484565b600e83527f4e65787573426f6f74737472617000000000000000000000000000000000000060208401528051610e0a8282611484565b600581527f312e332e30000000000000000000000000000000000000000000000000000000602082015281519485947f0f00000000000000000000000000000000000000000000000000000000000000865260e0602087015260e0860190610d8b565b9184830390850152610d8b565b4660608301523060808301525f60a083015281810360c083015260206060519182815201906080905f5b818110610eb2575050500390f35b8251845285945060209384019390920191600101610ea4565b9060a06003198301126101345760043567ffffffffffffffff81116101345782610ef7916004016103f4565b9290929160243567ffffffffffffffff81116101345782610f1a916004016103f4565b9290929160443567ffffffffffffffff81116101345782610f3d91600401610425565b9160643567ffffffffffffffff81116101345781610f5d916004016103f4565b929092916084359067ffffffffffffffff82116101345761022e916004016103f4565b610f8936610ecb565b96610f9b9891989692959394966122f9565b610fa3612394565b5f5b818110611137575050505f5b8281106110df575050505f5b82811061108757505050610fd36105b98261151d565b61105d575b505f5b818110610fe457005b80610ffa6105b960206105f8600195878961155a565b156110585761100d61061182858761155a565b5f5160206128fd5f395f51905f5261102682858761155a565b3561103760206105f885888a61155a565b604080519283526001600160a01b0391909116602083015290a15b01610fdb565b611052565b61107e610763826106a76107505f5160206128fd5f395f51905f529561151d565b0390a15f610fd8565b8061109b6105b96106a760019487876114f6565b156110da576110b16106c26106a78387876114f6565b5f5160206128fd5f395f51905f526110d06106ff6106a78488886114f6565b0390a15b01610fbd565b6110d4565b806110f36105b96106a760019487876114f6565b15611132576111096107b76106a78387876114f6565b5f5160206128fd5f395f51905f526111286107ea6106a78488886114f6565b0390a15b01610fb1565b61112c565b8061114b610a0b6106a760019486886114f6565b5f5160206128fd5f395f51905f5261116a6102986106a78487896114f6565b0390a101610fa5565b6109413661039c565b346101345761118a36610ecb565b9661119c9891989692959394966122f9565b6111a4612394565b5f5b818110611338575050505f5b8281106112e0575050505f5b828110611288575050506111d46105b98261151d565b61125e575b505f5b8181106111e557005b806111fb6105b960206105f8600195878961155a565b156112595761120e61061182858761155a565b5f5160206128fd5f395f51905f5261122782858761155a565b3561123860206105f885888a61155a565b604080519283526001600160a01b0391909116602083015290a15b016111dc565b611253565b61127f610763826106a76107505f5160206128fd5f395f51905f529561151d565b0390a15f6111d9565b8061129c6105b96106a760019487876114f6565b156112db576112b26106c26106a78387876114f6565b5f5160206128fd5f395f51905f526112d16106ff6106a78488886114f6565b0390a15b016111be565b6112d5565b806112f46105b96106a760019487876114f6565b156113335761130a6107b76106a78387876114f6565b5f5160206128fd5f395f51905f526113296107ea6106a78488886114f6565b0390a15b016111b2565b61132d565b8061134c610a0b6106a760019486886114f6565b5f5160206128fd5f395f51905f5261136b6102986106a78487896114f6565b0390a1016111a6565b3461013457604036600319011261013457600435611391816101b6565b6024359060016001600160a01b03821614158061145e575b610d5a578115610d3257906113bd816127d4565b6113e8610c3c5f946001600160a01b03165f525f51602061295d5f395f51905f5260205260405f2090565b6001600160a01b0381168015159081611452575b5080611449575b15610cbe57610cb2610c3c8261142061144394610c818988612806565b6001600160a01b03165f525f51602061295d5f395f51905f5260205260405f2090565b926113e8565b50828410611403565b6001915014155f6113fc565b5061146b610d82826128bc565b6113a9565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff821117610afa57604052565b908060209392818452848401375f828201840152601f01601f1916010190565b9160206109919381815201916114a6565b6040513d5f823e3d90fd5b634e487b7160e01b5f52603260045260245ffd5b91908110156115185760051b81013590603e1981360301821215610134570190565b6114e2565b35610991816101b6565b903590601e1981360301821215610134570180359067ffffffffffffffff82116101345760200191813603831361013457565b91908110156115185760051b81013590605e1981360301821215610134570190565b6040519061158b604083611484565b565b5f356001600160e01b03198116906115d6826001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b546115f56001600160a01b0382169160581b6001600160f81b03191690565b906001600160a01b03811661163e57505060e01c63bc197c81811463f23a6e6182141763150b7a0282141761163557506308c63e275f526020526024601cfd5b6020526020603cf35b5f51602061293d5f395f51905f52546001600160a01b0316801515926060929084611773575b6001600160f81b03198116607f60f91b0361170857505f809161168561284f565b90602082519201905afa926116986122ca565b935b15611700576116ac575b825160208401f35b803b15610134576116d75f92918392604051948580948193630b9dfbed60e11b83526004830161186e565b03925af1801561036d576116ec575b806116a4565b806103615f6116fa93611484565b816116e6565b835160208501fd5b6001600160f81b0319811661173e57505f809161172361284f565b906020825192019034905af1926117386122ca565b9361169a565b7fb96fcfe4000000000000000000000000000000000000000000000000000000005f526001600160f81b03191660045260245ffd5b925060405163d68f602560e01b81525f818061179436343360048501611848565b038183875af190811561036d575f916117af575b5092611664565b6117c391503d805f833e61038c8183611484565b856117a8565b67ffffffffffffffff8111610afa57601f01601f191660200190565b6020818303126101345780519067ffffffffffffffff8211610134570181601f8201121561013457805190611819826117c9565b926118276040519485611484565b8284526020838301011161013457815f9260208093018386015e8301015290565b61099193926001600160a01b03606093168252602082015281604082015201905f6114a6565b906020610991928181520190610d8b565b91906001600160a01b036118a76001600160a01b035f51602061293d5f395f51905f52541690565b16806118b7575061158b9261197b565b90916040519263d68f602560e01b84525f84806118d936343360048501611848565b038183875af193841561036d575f94611943575b506118f992939461197b565b803b15610134576119245f92918392604051948580948193630b9dfbed60e11b83526004830161186e565b03925af1801561036d576119355750565b806103615f61158b93611484565b6118f993945061195c903d805f833e61038c8183611484565b93926118ed565b90816020910312610134575180151581036101345790565b60405163ecd0596160e01b8152600160048201526001600160a01b038216939190602081602481885afa90811561036d575f91611a6c575b5015611a44576001600160a01b037f0000000000000000000000000000000055c766a7060797fbc7be40c08b296b72168414611a1c576119f290612407565b823b1561013457611924925f92836040518096819582946306d61fe760e41b8452600484016114c6565b7fabc3af79000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fe27c1dd8000000000000000000000000000000000000000000000000000000005f5260045ffd5b611a8e915060203d602011611a94575b611a868183611484565b810190611963565b5f6119b3565b503d611a7c565b91906001600160a01b03611ac36001600160a01b035f51602061293d5f395f51905f52541690565b1680611ad3575061158b92611b35565b90916040519263d68f602560e01b84525f8480611af536343360048501611848565b038183875af193841561036d575f94611b15575b506118f9929394611b35565b6118f9939450611b2e903d805f833e61038c8183611484565b9392611b09565b60405163ecd0596160e01b8152600260048201526001600160a01b038216939190602081602481885afa90811561036d575f91611b7c575b5015611a44576119f290612558565b611b95915060203d602011611a9457611a868183611484565b5f611b6d565b91906001600160a01b03611bc36001600160a01b035f51602061293d5f395f51905f52541690565b1680611bd3575061158b92611c72565b90916040519263d68f602560e01b84525f8480611bf536343360048501611848565b038183875af193841561036d575f94611c15575b506118f9929394611c72565b6118f9939450611c2e903d805f833e61038c8183611484565b9392611c09565b15611c3d5750565b6001600160a01b03907f741cbe03000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60405163ecd0596160e01b81526004808201526001600160a01b038216939190602081602481885afa90811561036d575f91611d18575b5015611a44576119f290611ce4611cd46001600160a01b035f51602061293d5f395f51905f52541690565b6001600160a01b03811615611c35565b6001600160a01b03166001600160a01b03195f51602061293d5f395f51905f525416175f51602061293d5f395f51905f5255565b611d31915060203d602011611a9457611a868183611484565b5f611ca9565b91906001600160a01b03611d5f6001600160a01b035f51602061293d5f395f51905f52541690565b1680611d6f575061158b92611f13565b90916040519263d68f602560e01b84525f8480611d9136343360048501611848565b038183875af193841561036d575f94611db1575b506118f9929394611f13565b6118f9939450611dca903d805f833e61038c8183611484565b9392611da5565b906004116101345790600490565b909291928360051161013457831161013457600501916004190190565b919091356001600160e01b031981169260048110611e18575050565b6001600160e01b0319929350829060040360031b1b161690565b90600410156115185760040190565b15611e4857565b7f867a1dcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b929192611e7c826117c9565b91611e8a6040519384611484565b829481845281830111610134578281602093845f960137010152565b15611ead57565b7fc001660b000000000000000000000000000000000000000000000000000000005f5260045ffd5b15611edd5750565b6001600160e01b0319907fa56a04dd000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90916001600160a01b0382169160405163ecd0596160e01b815260208180611f4360048201906003602083019252565b0381875afa90811561036d575f9161214f575b5015611a445761207b84612044611fd5611fce8680611fac611f9f611f9183611f8b611f856120da9e8c611dd1565b90611dfc565b99611e32565b356001600160f81b03191690565b6001600160f81b03191690565b9a6001600160f81b03198c16158015612138575b611fc990611e41565b611ddf565b3691611e70565b966120046001600160e01b031984166306d61fe760e41b811490811561210e575b8115612105575b5015611ea6565b61201983612014610d8282612648565b611ed5565b61203361202461157c565b6001600160a01b039096168652565b6001600160f81b0319166020850152565b6001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b8151815460209093015174ff000000000000000000000000000000000000000060589190911c167fffffffffffffffffffffff0000000000000000000000000000000000000000009093166001600160a01b0390911617919091179055565b803b15610134576119245f929183926040519485809481936306d61fe760e41b83526004830161186e565b9050155f611ffd565b7f8a91b0e30000000000000000000000000000000000000000000000000000000081149150611ff6565b50607f60f91b6001600160f81b03198d1614611fc0565b612168915060203d602011611a9457611a868183611484565b5f611f56565b9291906001600160a01b036121976001600160a01b035f51602061293d5f395f51905f52541690565b16806121a7575061158b93612248565b916040939193519363d68f602560e01b85525f85806121cb36343360048501611848565b038183885af194851561036d575f956121eb575b506118f9939495612248565b6118f9949550612204903d805f833e61038c8183611484565b94936121df565b156122135750565b6001600160a01b03907fc689cd97000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60405163ecd0596160e01b8152600481018290526001600160a01b038316949290602081602481895afa90811561036d575f916122ab575b5015611a4457816122a66122966119f294612692565b6001600160a01b0381161561220b565b6126f5565b6122c4915060203d602011611a9457611a868183611484565b5f612280565b3d156122f4573d906122db826117c9565b916122e96040519384611484565b82523d5f602084013e565b606090565b60015f525f51602061295d5f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13546001600160a01b031661236c5760015f525f51602061295d5f395f51905f5260205261158b60405f2060016001600160a01b0319825416179055565b7f53c85e66000000000000000000000000000000000000000000000000000000005f5260045ffd5b60015f525f51602061291d5f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7546001600160a01b031661236c5760015f525f51602061291d5f395f51905f5260205261158b60405f2060016001600160a01b0319825416179055565b6001600160a01b0381168015801561254e575b61253c575f9081525f51602061291d5f395f51905f5260205260409020546001600160a01b03166125085760015f525f51602061291d5f395f51905f5260205261158b906124cd61248a7ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c7610c3c565b6124b2835f51602061291d5f395f51905f52906001600160a01b03165f5260205260405f2090565b906001600160a01b03166001600160a01b0319825416179055565b60015f525f51602061291d5f395f51905f526020527ffe44ceacbf4f03c6ac19f86826dd265fa9ec25125e8b1766c207f24cd3bc73c76124b2565b7f40d3d1a4000000000000000000000000000000000000000000000000000000005f526001600160a01b031660045260245ffd5b637c84ecfb60e01b5f5260045260245ffd5b506001811461241a565b6001600160a01b0381168015801561263e575b61253c575f9081525f51602061295d5f395f51905f5260205260409020546001600160a01b03166125085760015f525f51602061295d5f395f51905f5260205261158b906126036125db7fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a13610c3c565b6124b2835f51602061295d5f395f51905f52906001600160a01b03165f5260205260405f2090565b60015f525f51602061295d5f395f51905f526020527fbdfeb076d903611fa58576955630d640569633049bcf40ad9c22db9251b54a136124b2565b506001811461256b565b61268b6001600160a01b03916001600160e01b0319165f527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260405f2090565b5416151590565b6008036126c7576001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f06541690565b6001600160a01b037f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f05541690565b6008810361275857506001600160a01b03166001600160a01b03197f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f065416177f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0655565b6009146127625750565b6001600160a01b03166001600160a01b03197f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f055416177f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0555565b67ffffffffffffffff8111610afa5760051b60200190565b906127de826127bc565b6127eb6040519182611484565b82815280926127fc601f19916127bc565b0190602036910137565b80518210156115185760209160051b010190565b634e487b7160e01b5f52601160045260245ffd5b5f19811461283c5760010190565b61281a565b5f1981019190821161283c57565b60405190602036830101604052816014360181525f602036920137604051601481016040523360601b9052565b6001600160a01b031680600114159081612894575090565b90505f525f51602061291d5f395f51905f526020526001600160a01b0360405f205416151590565b6001600160a01b0316806001141590816128d4575090565b90505f525f51602061295d5f395f51905f526020526001600160a01b0360405f20541615159056fed21d0b289f126c4b473ea641963e766833c2f13866e4ff480abd787c100ef1230bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f000bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f030bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f01a164736f6c634300081b000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000055c766a7060797fbc7be40c08b296b7200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000
-----Decoded View---------------
Arg [0] : defaultValidator (address): 0x0000000055C766a7060797FBc7Be40c08B296b72
Arg [1] : initData (bytes): 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000055c766a7060797fbc7be40c08b296b72
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000014
Arg [3] : 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.