Source Code
Overview
S Balance
0 S
More Info
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
6528107 | 45 hrs ago | Contract Creation | 0 S |
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 "../interfaces/modules/IModule.sol"; import { IERC7484 } from "../interfaces/IERC7484.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; } /// @title NexusBootstrap /// @notice Manages the installation of modules into Nexus smart accounts using delegatecalls. contract NexusBootstrap is ModuleManager { /// @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. /// @param data The initialization data for the validator module. function initNexusWithSingleValidator( IModule validator, bytes calldata data, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external { _configureRegistry(registry, attesters, threshold); _installValidator(address(validator), data); } /// @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. /// @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. function initNexus( BootstrapConfig[] calldata validators, BootstrapConfig[] calldata executors, BootstrapConfig calldata hook, BootstrapConfig[] calldata fallbacks, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external { _configureRegistry(registry, attesters, threshold); // Initialize validators for (uint256 i = 0; i < validators.length; i++) { _installValidator(validators[i].module, validators[i].data); } // Initialize executors for (uint256 i = 0; i < executors.length; i++) { if (executors[i].module == address(0)) continue; _installExecutor(executors[i].module, executors[i].data); } // Initialize hook if (hook.module != address(0)) { _installHook(hook.module, hook.data); } // 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); } } /// @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. /// @param hook The configuration for the hook module. function initNexusScoped( BootstrapConfig[] calldata validators, BootstrapConfig calldata hook, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external { _configureRegistry(registry, attesters, threshold); // Initialize validators for (uint256 i = 0; i < validators.length; i++) { _installValidator(validators[i].module, validators[i].data); } // Initialize hook if (hook.module != address(0)) { _installHook(hook.module, hook.data); } } /// @notice Prepares calldata for the initNexus function. /// @param validators The configuration array for validator modules. /// @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. /// @return init The prepared calldata for initNexus. function getInitNexusCalldata( BootstrapConfig[] calldata validators, BootstrapConfig[] calldata executors, BootstrapConfig calldata hook, BootstrapConfig[] calldata fallbacks, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external view returns (bytes memory init) { init = abi.encode(address(this), abi.encodeCall(this.initNexus, (validators, executors, hook, fallbacks, registry, attesters, threshold))); } /// @notice Prepares calldata for the initNexusScoped function. /// @param validators The configuration array for validator modules. /// @param hook The configuration for the hook module. /// @return init The prepared calldata for initNexusScoped. function getInitNexusScopedCalldata( BootstrapConfig[] calldata validators, BootstrapConfig calldata hook, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external view returns (bytes memory init) { init = abi.encode(address(this), abi.encodeCall(this.initNexusScoped, (validators, hook, registry, attesters, threshold))); } /// @notice Prepares calldata for the initNexusWithSingleValidator function. /// @param validator The configuration for the validator module. /// @return init The prepared calldata for initNexusWithSingleValidator. function getInitNexusWithSingleValidatorCalldata( BootstrapConfig calldata validator, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external view returns (bytes memory init) { init = abi.encode( address(this), abi.encodeCall(this.initNexusWithSingleValidator, (IModule(validator.module), validator.data, registry, attesters, threshold)) ); } /// @dev EIP712 domain name and version. function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) { name = "NexusBootstrap"; version = "1.0.0"; } }
// 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 { IHook } from "../interfaces/modules/IHook.sol"; import { IModule } from "../interfaces/modules/IModule.sol"; import { IExecutor } from "../interfaces/modules/IExecutor.sol"; import { IFallback } from "../interfaces/modules/IFallback.sol"; import { IValidator } from "../interfaces/modules/IValidator.sol"; import { CallType, CALLTYPE_SINGLE, CALLTYPE_STATIC } from "../lib/ModeLib.sol"; import { ExecLib } from "../lib/ExecLib.sol"; import { LocalCallDataParserLib } from "../lib/local/LocalCallDataParserLib.sol"; import { IModuleManagerEventsAndErrors } from "../interfaces/base/IModuleManagerEventsAndErrors.sol"; import { MODULE_TYPE_VALIDATOR, MODULE_TYPE_EXECUTOR, MODULE_TYPE_FALLBACK, MODULE_TYPE_HOOK, MODULE_TYPE_MULTI, MODULE_ENABLE_MODE_TYPE_HASH, ERC1271_MAGICVALUE } from "../types/Constants.sol"; import { EIP712 } from "solady/utils/EIP712.sol"; import { ExcessivelySafeCall } from "excessively-safe-call/ExcessivelySafeCall.sol"; import { RegistryAdapter } from "./RegistryAdapter.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, IModuleManagerEventsAndErrors, RegistryAdapter { using SentinelListLib for SentinelListLib.SentinelList; using LocalCallDataParserLib for bytes; using ExecLib for address; using ExcessivelySafeCall for address; /// @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); } } receive() external payable {} /// @dev Fallback function to manage incoming calls using designated handlers based on the call type. fallback(bytes calldata callData) external payable withHook returns (bytes memory) { return _fallback(callData); } /// @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 _initModuleManager() 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(); if (!_checkEnableModeSignature(_getEnableModeDataHash(module, moduleType, userOpHash, moduleInitData), enableModeSignature)) revert EnableModeSigError(); _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 /// @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 withHook { 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 (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 withRegistry(validator, MODULE_TYPE_VALIDATOR) { if (!IValidator(validator).isModuleType(MODULE_TYPE_VALIDATOR)) revert MismatchModuleTypeId(MODULE_TYPE_VALIDATOR); _getAccountStorage().validators.push(validator); IValidator(validator).onInstall(data); } /// @dev Uninstalls a validator module /!\ ensuring the account retains at least one validator. /// @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); // Sentinel pointing to itself / zero means the list is empty / uninitialized, so check this after removal // Below error is very specific to uninstalling validators. require(_hasValidators(), CanNotRemoveLastValidator()); 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 withRegistry(executor, MODULE_TYPE_EXECUTOR) { if (!IExecutor(executor).isModuleType(MODULE_TYPE_EXECUTOR)) revert MismatchModuleTypeId(MODULE_TYPE_EXECUTOR); _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 withRegistry(hook, MODULE_TYPE_HOOK) { if (!IHook(hook).isModuleType(MODULE_TYPE_HOOK)) revert MismatchModuleTypeId(MODULE_TYPE_HOOK); 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 data De-initialization data to configure the hook upon uninstallation. function _uninstallHook(address hook, bytes calldata data) internal virtual { _setHook(address(0)); 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 withRegistry(handler, MODULE_TYPE_FALLBACK) { if (!IFallback(handler).isModuleType(MODULE_TYPE_FALLBACK)) revert MismatchModuleTypeId(MODULE_TYPE_FALLBACK); // 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:])); } /// @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]); } } } /// @notice Checks if an enable mode signature is valid. /// @param structHash data hash. /// @param sig Signature. function _checkEnableModeSignature(bytes32 structHash, bytes calldata sig) internal view returns (bool) { address enableModeSigValidator = address(bytes20(sig[0:20])); if (!_isValidatorInstalled(enableModeSigValidator)) { revert ValidatorNotInstalled(enableModeSigValidator); } 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(enableModeSigValidator).isValidSignatureWithSender(address(this), eip712Digest, sig[20:]) returns (bytes4 res) { return res == ERC1271_MAGICVALUE; } catch { return false; } } /// @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 view returns (bytes32) { return keccak256(abi.encode(MODULE_ENABLE_MODE_TYPE_HASH, module, moduleType, userOpHash, keccak256(initData))); } /// @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 { return false; } } /// @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 there is at least one validator installed. /// @return True if there is at least one validator, otherwise false. function _hasValidators() internal view returns (bool) { return _getAccountStorage().validators.getNext(address(0x01)) != address(0x01) && _getAccountStorage().validators.getNext(address(0x01)) != address(0x00); } /// @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); } function _fallback(bytes calldata callData) private returns (bytes memory result) { bool success; FallbackHandler storage $fallbackHandler = _getAccountStorage().fallbacks[msg.sig]; address handler = $fallbackHandler.handler; CallType calltype = $fallbackHandler.calltype; if (handler != address(0)) { //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 if (!success) { assembly { revert(add(result, 0x20), mload(result)) } } } else { // If there's no handler, the call can be one of onERCXXXReceived() 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))) { success := true // it is one of onERCXXXReceived result := mload(0x40) //result was set to 0x60 as it was empty, so we need to find a new space for it mstore(result, 0x04) //store length mstore(add(result, 0x20), shl(224, s)) //store calldata mstore(0x40, add(result, 0x24)) //allocate memory } } // if there was no handler and it is not the onERCXXXReceived call, revert require(success, MissingFallbackHandler(msg.sig)); } } /// @dev Helper function to paginate entries in a SentinelList. /// @param list The SentinelList to paginate. /// @param cursor The cursor to start paginating from. /// @param size The number of entries to return. /// @return array The array of addresses in the list. /// @return nextCursor The cursor for the next page of entries. function _paginate( SentinelListLib.SentinelList storage list, address cursor, uint256 size ) private view returns (address[] memory array, address nextCursor) { (array, nextCursor) = list.getEntriesPaginated(cursor, size); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] /// @title Nexus - ERC-7579 Module Base Interface /// @notice Interface for module management in smart accounts, complying with ERC-7579 specifications. /// @dev Defines the lifecycle hooks and checks for modules within the smart account architecture. /// This interface includes methods for installing, uninstalling, and verifying module types and initialization status. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IModule { /// @notice Installs the module with necessary initialization data. /// @dev Reverts if the module is already initialized. /// @param data Arbitrary data required for initializing the module during `onInstall`. function onInstall(bytes calldata data) external; /// @notice Uninstalls the module and allows for cleanup via arbitrary data. /// @dev Reverts if any issues occur that prevent clean uninstallation. /// @param data Arbitrary data required for deinitializing the module during `onUninstall`. function onUninstall(bytes calldata data) external; /// @notice Determines if the module matches a specific module type. /// @dev Should return true if the module corresponds to the type ID, false otherwise. /// @param moduleTypeId Numeric ID of the module type as per ERC-7579 specifications. /// @return True if the module is of the specified type, false otherwise. function isModuleType(uint256 moduleTypeId) external view returns (bool); /// @notice Checks if the module has been initialized for a specific smart account. /// @dev Returns true if initialized, false otherwise. /// @param smartAccount Address of the smart account to check for initialization status. /// @return True if the module is initialized for the given smart account, false otherwise. function isInitialized(address smartAccount) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; interface IERC7484 { event NewTrustedAttesters(); /** * Allows Smart Accounts - the end users of the registry - to appoint * one or many attesters as trusted. * @dev this function reverts, if address(0), or duplicates are provided in attesters[] * * @param threshold The minimum number of attestations required for a module * to be considered secure. * @param attesters The addresses of the attesters to be trusted. */ function trustAttesters(uint8 threshold, address[] calldata attesters) external; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Check with Registry internal attesters */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function check(address module) external view; function checkForAccount(address smartAccount, address module) external view; function check(address module, uint256 moduleType) external view; function checkForAccount(address smartAccount, address module, uint256 moduleType) external view; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Check with external attester(s) */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function check(address module, address[] calldata attesters, uint256 threshold) external view; function check(address module, uint256 moduleType, address[] calldata attesters, uint256 threshold) external view; }
// 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/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; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // 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 { IModule } from "./IModule.sol"; /// @title Hook Management Interface /// @notice Provides methods for pre-checks and post-checks of transactions to ensure conditions and state consistency. /// @dev Defines two critical lifecycle hooks in the transaction process: `preCheck` and `postCheck`. /// These methods facilitate validating conditions prior to execution and verifying state changes afterwards, respectively. interface IHook is IModule { /// @notice Performs checks before a transaction is executed, potentially modifying the transaction context. /// @dev This method is called before the execution of a transaction to validate and possibly adjust execution context. /// @param msgSender The original sender of the transaction. /// @param msgValue The amount of wei sent with the call. /// @param msgData The calldata of the transaction. /// @return hookData Data that may be used or modified throughout the transaction lifecycle, passed to `postCheck`. function preCheck(address msgSender, uint256 msgValue, bytes calldata msgData) external returns (bytes memory hookData); /// @notice Performs checks after a transaction is executed to ensure state consistency and log results. /// @dev This method is called after the execution of a transaction to verify and react to the execution outcome. /// @param hookData Data returned from `preCheck`, containing execution context or modifications. function postCheck(bytes calldata hookData) external; }
// 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 { IModule } from "./IModule.sol"; /// @title Nexus - IExecutor Interface /// @notice Defines the interface for Executor modules within the Nexus Smart Account framework, compliant with the ERC-7579 standard. /// @dev Extends IModule to include functionalities specific to execution modules. /// This interface is future-proof, allowing for expansion and integration of advanced features in subsequent versions. /// @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 IExecutor is IModule { // Future methods for execution management will be defined here to accommodate evolving requirements. }
// 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 { IModule } from "./IModule.sol"; /// @title Nexus - IFallback Interface /// @notice Defines the interface for Fallback modules within the Nexus Smart Account framework, compliant with the ERC-7579 standard. /// @dev Extends IModule to include functionalities specific to fallback modules. /// This interface is future-proof, allowing for expansion and integration of advanced features in subsequent versions. /// @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 IFallback is IModule { // Future methods for fallback management will be defined here to accommodate evolving blockchain technologies. }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; import { IModule } from "./IModule.sol"; /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IValidator is IModule { /// @notice Validates a user operation as per ERC-4337 standard requirements. /// @dev Should ensure that the signature and nonce are verified correctly before the transaction is allowed to proceed. /// The function returns a status code indicating validation success or failure. /// @param userOp The user operation containing transaction details to be validated. /// @param userOpHash The hash of the user operation data, used for verifying the signature. /// @return status The result of the validation process, typically indicating success or the type of failure. function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); /// @notice Verifies a signature against a hash, using the sender's address as a contextual check. /// @dev Used to confirm the validity of a signature against the specific conditions set by the sender. /// @param sender The address from which the operation was initiated, adding an additional layer of validation against the signature. /// @param hash The hash of the data signed. /// @param data The signature data to validate. /// @return magicValue A bytes4 value that corresponds to the ERC-1271 standard, indicating the validity of the signature. function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data) external view returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; /// @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 "../types/DataTypes.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 { error InvalidBatchCallData(); 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())) } } function decodeBatch(bytes calldata callData) internal view returns (Execution[] calldata executionBatch) { /* * Batch Call Calldata Layout * Offset (in bytes) | Length (in bytes) | Contents * 0x0 | 0x4 | bytes4 function selector * 0x4 | - | abi.encode(IERC7579Execution.Execution[]) */ assembly ("memory-safe") { let dataPointer := add(callData.offset, calldataload(callData.offset)) // Extract the ERC7579 Executions executionBatch.offset := add(dataPointer, 32) executionBatch.length := calldataload(dataPointer) } } 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 { CallType } from "../../lib/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); /// @notice Thrown when attempting to remove the last validator. error CanNotRemoveLastValidator(); /// @dev Thrown when the specified module address is not recognized as valid. error ValidatorNotInstalled(address module); /// @dev Thrown when there is no installed validator detected. error NoValidatorInstalled(); /// @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 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 a fallback handler fails to uninstall properly. error FallbackHandlerUninstallFailed(); /// @dev Thrown when no fallback handler is available for a given selector. error MissingFallbackHandler(bytes4 selector); /// @dev Thrown when Invalid data is provided for MultiType install flow error InvalidInput(); /// @dev Thrown when unable to validate Module Enable Mode signature error EnableModeSigError(); /// Error thrown when account installs/uninstalls module with mismatched input `moduleTypeId` error MismatchModuleTypeId(uint256 moduleTypeId); /// @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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] // Magic value for ERC-1271 valid signature bytes4 constant ERC1271_MAGICVALUE = 0x1626ba7e; // Value indicating an invalid ERC-1271 signature bytes4 constant ERC1271_INVALID = 0xFFFFFFFF; // Value indicating successful validation uint256 constant VALIDATION_SUCCESS = 0; // Value indicating failed validation uint256 constant VALIDATION_FAILED = 1; // Module type identifier for Multitype install uint256 constant MODULE_TYPE_MULTI = 0; // Module type identifier for validators uint256 constant MODULE_TYPE_VALIDATOR = 1; // Module type identifier for executors uint256 constant MODULE_TYPE_EXECUTOR = 2; // Module type identifier for fallback handlers uint256 constant MODULE_TYPE_FALLBACK = 3; // Module type identifier for hooks uint256 constant MODULE_TYPE_HOOK = 4; string constant MODULE_ENABLE_MODE_NOTATION = "ModuleEnableMode(address module,uint256 moduleType,bytes32 userOpHash,bytes32 initDataHash)"; bytes32 constant MODULE_ENABLE_MODE_TYPE_HASH = keccak256(bytes(MODULE_ENABLE_MODE_NOTATION)); // Validation modes bytes1 constant MODE_VALIDATION = 0x00; bytes1 constant MODE_MODULE_ENABLE = 0x01; bytes4 constant SUPPORTS_ERC7739 = 0x77390000; bytes4 constant SUPPORTS_ERC7739_V1 = 0x77390001;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.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; 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) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EIP-5267 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev See: https://eips.ethereum.org/EIPS/eip-5267 function eip712Domain() public view virtual returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { fields = hex"0f"; // `0b01111`. (name, version) = _domainNameAndVersion(); chainId = block.chainid; verifyingContract = address(this); salt = salt; // `bytes32(0)`. extensions = extensions; // `new uint256[](0)`. } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the EIP-712 domain separator. function _buildDomainSeparator() private view returns (bytes32 separator) { // We will use `separator` to store the name hash to save a bit of gas. bytes32 versionHash; if (_domainNameAndVersionMayChange()) { (string memory name, string memory version) = _domainNameAndVersion(); separator = keccak256(bytes(name)); versionHash = keccak256(bytes(version)); } else { separator = _cachedNameHash; versionHash = _cachedVersionHash; } /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Load the free memory pointer. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), separator) // Name hash. mstore(add(m, 0x40), versionHash) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) separator := keccak256(m, 0xa0) } } /// @dev Returns if the cached domain separator has been invalidated. function _cachedDomainSeparatorInvalidated() private view returns (bool result) { uint256 cachedChainId = _cachedChainId; uint256 cachedThis = _cachedThis; /// @solidity memory-safe-assembly assembly { result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis))) } } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.7.6; library ExcessivelySafeCall { uint256 constant LOW_28_MASK = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _value The value in wei to send to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeCall( address _target, uint256 _gas, uint256 _value, uint16 _maxCopy, bytes memory _calldata ) internal returns (bool, bytes memory) { // set up for assembly call uint256 _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := call( _gas, // gas _target, // recipient _value, // ether value add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeStaticCall( address _target, uint256 _gas, uint16 _maxCopy, bytes memory _calldata ) internal view returns (bool, bytes memory) { // set up for assembly call uint256 _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := staticcall( _gas, // gas _target, // recipient add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /** * @notice Swaps function selectors in encoded contract calls * @dev Allows reuse of encoded calldata for functions with identical * argument types but different names. It simply swaps out the first 4 bytes * for the new selector. This function modifies memory in place, and should * only be used with caution. * @param _newSelector The new 4-byte selector * @param _buf The encoded contract args */ function swapSelector(bytes4 _newSelector, bytes memory _buf) internal pure { require(_buf.length >= 4); uint256 _mask = LOW_28_MASK; assembly { // load the first word of let _word := mload(add(_buf, 0x20)) // mask out the top 4 bytes // /x _word := and(_word, _mask) _word := or(_newSelector, _word) mstore(add(_buf, 0x20), _word) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import { IERC7484 } from "../interfaces/IERC7484.sol"; /// @title RegistryAdapter /// @notice This contract provides an interface for interacting with an ERC-7484 compliant registry. /// @dev The registry feature is opt-in, allowing the smart account owner to select and trust specific attesters. abstract contract RegistryAdapter { IERC7484 public registry; /// @notice Emitted when a new ERC-7484 registry is configured for the account. /// @param registry The configured registry contract. event ERC7484RegistryConfigured(IERC7484 indexed registry); /// @notice Modifier to check if a module meets the required attestations in the registry. /// @param module The module to check. /// @param moduleType The type of the module to verify in the registry. modifier withRegistry(address module, uint256 moduleType) { _checkRegistry(module, moduleType); _; } /// @notice Configures the ERC-7484 registry and sets trusted attesters. /// @param newRegistry The new registry contract to use. /// @param attesters The list of attesters to trust. /// @param threshold The number of attestations required. function _configureRegistry(IERC7484 newRegistry, address[] calldata attesters, uint8 threshold) internal { registry = newRegistry; if (address(newRegistry) != address(0)) { newRegistry.trustAttesters(threshold, attesters); } emit ERC7484RegistryConfigured(newRegistry); } /// @notice Checks the registry to ensure sufficient valid attestations for a module. /// @param module The module to check. /// @param moduleType The type of the module to verify in the registry. /// @dev Reverts if the required attestations are not met. function _checkRegistry(address module, uint256 moduleType) internal view { IERC7484 moduleRegistry = registry; if (address(moduleRegistry) != address(0)) { // This will revert if attestations or the threshold are not met. moduleRegistry.check(module, moduleType); } } }
// 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 } from "../modules/IHook.sol"; import { CallType } from "../../lib/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 { SentinelListLib.SentinelList validators; ///< List of validators, initialized upon contract deployment. SentinelListLib.SentinelList executors; ///< List of executors, similarly initialized. mapping(bytes4 => FallbackHandler) fallbacks; ///< Mapping of selectors to their respective fallback handlers. IHook hook; ///< Current hook module associated with this account. mapping(address hook => uint256) emergencyUninstallTimelock; ///< Mapping of hooks to requested timelocks. } /// @notice Defines a fallback handler with an associated handler address and a call type. struct FallbackHandler { address handler; ///< The address of the fallback function handler. CallType calltype; ///< The type of call this handler supports (e.g., static or call). } }
// 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 Execution /// @notice Struct to encapsulate execution data for a transaction struct Execution { /// @notice The target address for the transaction address target; /// @notice The value in wei to send with the transaction uint256 value; /// @notice The calldata for the transaction bytes callData; }
{ "remappings": [ "@openzeppelin/=node_modules/@openzeppelin/", "forge-std/=node_modules/forge-std/src/", "account-abstraction/=node_modules/account-abstraction/contracts/", "solady/=node_modules/solady/src/", "excessively-safe-call/=node_modules/excessively-safe-call/src/", "sentinellist/=node_modules/sentinellist/src/", "solarray/=node_modules/solarray/src/", "erc7739Validator/=node_modules/erc7739-validator-base/src/", "@ERC4337/=node_modules/@ERC4337/", "@gnosis.pm/=node_modules/@gnosis.pm/", "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/", "@prb/=node_modules/@prb/", "@prb/math/=node_modules/erc7739-validator-base/node_modules/@prb/math/src/", "@rhinestone/=node_modules/@rhinestone/", "@safe-global/=node_modules/@safe-global/", "@zerodev/=node_modules/@zerodev/", "ExcessivelySafeCall/=node_modules/erc7739-validator-base/node_modules/excessively-safe-call/src/", "account-abstraction-v0.6/=node_modules/account-abstraction-v0.6/", "ds-test/=node_modules/ds-test/", "enumerablemap/=node_modules/enumerablemap/", "enumerableset4337/=node_modules/erc7739-validator-base/node_modules/enumerablemap/src/", "erc4337-validation/=node_modules/erc7739-validator-base/node_modules/@rhinestone/erc4337-validation/src/", "erc7579/=node_modules/erc7579/", "erc7739-validator-base/=node_modules/erc7739-validator-base/", "eth-gas-reporter/=node_modules/eth-gas-reporter/", "hardhat-deploy/=node_modules/hardhat-deploy/", "hardhat/=node_modules/hardhat/", "kernel/=node_modules/erc7739-validator-base/node_modules/@zerodev/kernel/src/", "module-bases/=node_modules/erc7739-validator-base/node_modules/@rhinestone/module-bases/src/", "modulekit/=node_modules/modulekit/", "registry/=node_modules/modulekit/node_modules/@rhinestone/registry/src/", "safe7579/=node_modules/erc7739-validator-base/node_modules/@rhinestone/safe7579/src/", "stringutils/=node_modules/stringutils/" ], "optimizer": { "enabled": true, "runs": 999 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
[{"inputs":[],"name":"CanNotRemoveLastValidator","type":"error"},{"inputs":[],"name":"EnableModeSigError","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"FallbackAlreadyInstalledForSelector","type":"error"},{"inputs":[],"name":"FallbackCallTypeInvalid","type":"error"},{"inputs":[],"name":"FallbackHandlerUninstallFailed","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":[{"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":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"}],"name":"MismatchModuleTypeId","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"MissingFallbackHandler","type":"error"},{"inputs":[],"name":"ModuleAddressCanNotBeZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"internalType":"address","name":"module","type":"address"}],"name":"ModuleAlreadyInstalled","type":"error"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"internalType":"address","name":"module","type":"address"}],"name":"ModuleNotInstalled","type":"error"},{"inputs":[],"name":"NoValidatorInstalled","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"UnauthorizedOperation","type":"error"},{"inputs":[{"internalType":"CallType","name":"callType","type":"bytes1"}],"name":"UnsupportedCallType","type":"error"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"ValidatorNotInstalled","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC7484","name":"registry","type":"address"}],"name":"ERC7484RegistryConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"ModuleInstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"ModuleUninstalled","type":"event"},{"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":[{"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":[{"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[]"},{"internalType":"contract IERC7484","name":"registry","type":"address"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"getInitNexusCalldata","outputs":[{"internalType":"bytes","name":"init","type":"bytes"}],"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":"hook","type":"tuple"},{"internalType":"contract IERC7484","name":"registry","type":"address"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"getInitNexusScopedCalldata","outputs":[{"internalType":"bytes","name":"init","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BootstrapConfig","name":"validator","type":"tuple"},{"internalType":"contract IERC7484","name":"registry","type":"address"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"getInitNexusWithSingleValidatorCalldata","outputs":[{"internalType":"bytes","name":"init","type":"bytes"}],"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[]"},{"internalType":"contract IERC7484","name":"registry","type":"address"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"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":"hook","type":"tuple"},{"internalType":"contract IERC7484","name":"registry","type":"address"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"initNexusScoped","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IModule","name":"validator","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"contract IERC7484","name":"registry","type":"address"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"initNexusWithSingleValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract IERC7484","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
610120604052348015600f575f5ffd5b50306080524660a0526060806061604080518082018252600e81526d04e65787573426f6f7473747261760941b602080830191909152825180840190935260058352640312e302e360dc1b9083015291565b815160209283012081519183019190912060c082905260e0819052604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8152938401929092529082015246606082015230608082015260a09020610100525060c99050565b60805160a05160c05160e051610100516122d76100f75f395f50505f50505f50505f50505f50506122d75ff3fe6080604052600436106100ca575f3560e01c8063837b892e116100735780639e2533ed1161004d5780639e2533ed14610425578063d6fe71f114610444578063ea5f61d014610463576100d1565b8063837b892e146103b357806384b0196e146103df57806385a924cc14610406576100d1565b806355470cf1116100a457806355470cf1146103495780635faac46b146103685780637b10399914610395576100d1565b806301fe9ff2146102455780630a664dba14610266578063481ddd2314610297576100d1565b366100d157005b5f3660605f6101077f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f03546001600160a01b031690565b90506001600160a01b038116610128576101218484610482565b9150610239565b6040517fd68f60250000000000000000000000000000000000000000000000000000000081525f906001600160a01b0383169063d68f6025906101759033903490869036906004016118b1565b5f604051808303815f875af1158015610190573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526101b791908101906118f7565b90506101c38585610482565b92506040517f173bf7da0000000000000000000000000000000000000000000000000000000081526001600160a01b0383169063173bf7da9061020a9084906004016119d8565b5f604051808303815f87803b158015610221575f5ffd5b505af1158015610233573d5f5f3e3d5ffd5b50505050505b50915050805190602001f35b348015610250575f5ffd5b5061026461025f366004611a69565b6106b2565b005b348015610271575f5ffd5b5061027a6106d2565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102a2575f5ffd5b506103216102b1366004611b3a565b6001600160e01b0319165f9081527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260209081526040918290208251808401909352546001600160a01b038116808452600160a01b90910460f81b6001600160f81b031916929091018290529091565b604080516001600160f81b031990931683526001600160a01b0390911660208301520161028e565b348015610354575f5ffd5b50610264610363366004611b77565b610709565b348015610373575f5ffd5b50610387610382366004611beb565b6107d2565b60405161028e929190611c15565b3480156103a0575f5ffd5b505f5461027a906001600160a01b031681565b3480156103be575f5ffd5b506103d26103cd366004611c74565b61080b565b60405161028e91906119d8565b3480156103ea575f5ffd5b506103f36108a0565b60405161028e9796959493929190611d01565b348015610411575f5ffd5b506103d2610420366004611b77565b610948565b348015610430575f5ffd5b506103d261043f366004611d9b565b6109d1565b34801561044f575f5ffd5b5061026461045e366004611d9b565b610a66565b34801561046e575f5ffd5b5061038761047d366004611beb565b610c92565b5f80356001600160e01b03191681527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260408120805460609291906001600160a01b03811690600160a01b900460f81b8115610616576104e981607f60f91b610cc0565b1561055657816001600160a01b03166105028888610cd7565b60405161050f9190611eb4565b5f60405180830381855afa9150503d805f8114610547576040519150601f19603f3d011682016040523d82523d5f602084013e61054c565b606091505b5095509350610604565b610560815f610cc0565b156105c157816001600160a01b03163461057a8989610cd7565b6040516105879190611eb4565b5f6040518083038185875af1925050503d805f8114610547576040519150601f19603f3d011682016040523d82523d5f602084013e61054c565b6040517fb96fcfe40000000000000000000000000000000000000000000000000000000081526001600160f81b0319821660048201526024015b60405180910390fd5b8361061157845160208601fd5b6106a8565b5f3560e01c63150b7a02811463f23a6e61821463bc197c81831417171561065557600194506040519550600486528060e01b6020870152602486016040525b6001600160e01b03195f3516856106a5576040517f08c63e270000000000000000000000000000000000000000000000000000000081526001600160e01b031990911660048201526024016105fb565b50505b5050505092915050565b6106be84848484610d0c565b6106c9878787610deb565b50505050505050565b5f6107047f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f03546001600160a01b031690565b905090565b61071584848484610d0c565b5f5b868110156107935761078b88888381811061073457610734611eca565b90506020028101906107469190611ede565b610754906020810190611efc565b89898481811061076657610766611eca565b90506020028101906107789190611ede565b610786906020810190611f17565b610deb565b600101610717565b505f6107a26020870187611efc565b6001600160a01b0316146106c9576106c96107c06020870187611efc565b6107cd6020880188611f17565b610f0b565b60605f6108007f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0085856110ca565b909590945092505050565b606030806301fe9ff261082160208a018a611efc565b61082e60208b018b611f17565b8a8a8a8a6040516024016108489796959493929190611fa2565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050604051602001610886929190611ffe565b604051602081830303815290604052905095945050505050565b7f0f000000000000000000000000000000000000000000000000000000000000006060805f808083610936604080518082018252600e81527f4e65787573426f6f7473747261700000000000000000000000000000000000006020808301919091528251808401909352600583527f312e302e300000000000000000000000000000000000000000000000000000009083015291565b97989097965046955030945091925090565b606030306001600160a01b03166355470cf18a8a8a8a8a8a8a6040516024016109779796959493929190612108565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506040516020016109b5929190611ffe565b6040516020818303038152906040529050979650505050505050565b606030306001600160a01b031663d6fe71f18e8e8e8e8e8e8e8e8e8e8e604051602401610a089b9a99989796959493929190612151565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050604051602001610a46929190611ffe565b60405160208183030381529060405290509b9a5050505050505050505050565b610a7284848484610d0c565b5f5b8a811015610acb57610ac38c8c83818110610a9157610a91611eca565b9050602002810190610aa39190611ede565b610ab1906020810190611efc565b8d8d8481811061076657610766611eca565b600101610a74565b505f5b88811015610b8b575f8a8a83818110610ae957610ae9611eca565b9050602002810190610afb9190611ede565b610b09906020810190611efc565b6001600160a01b031614610b8357610b838a8a83818110610b2c57610b2c611eca565b9050602002810190610b3e9190611ede565b610b4c906020810190611efc565b8b8b84818110610b5e57610b5e611eca565b9050602002810190610b709190611ede565b610b7e906020810190611f17565b6110e4565b600101610ace565b505f610b9a6020890189611efc565b6001600160a01b031614610bc557610bc5610bb86020890189611efc565b6107cd60208a018a611f17565b5f5b85811015610c84575f878783818110610be257610be2611eca565b9050602002810190610bf49190611ede565b610c02906020810190611efc565b6001600160a01b031614610c7c57610c7c878783818110610c2557610c25611eca565b9050602002810190610c379190611ede565b610c45906020810190611efc565b888884818110610c5757610c57611eca565b9050602002810190610c699190611ede565b610c77906020810190611f17565b6111a2565b600101610bc7565b505050505050505050505050565b60605f6108007f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0185856110ca565b6001600160f81b0319828116908216145b92915050565b6060604080513681016020019091529050601436018152365f602083013760408051601481019091523360601b905292915050565b5f805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03861690811790915515610db2576040517ff05c04e10000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063f05c04e190610d84908490879087906004016121e0565b5f604051808303815f87803b158015610d9b575f5ffd5b505af1158015610dad573d5f5f3e3d5ffd5b505050505b6040516001600160a01b038516907ff98c8404c5b1bfef2e6ba9233c6e88845aedfd36eea8b192725d8c199571cf32905f90a250505050565b826001610df8828261150d565b60405163ecd0596160e01b8152600160048201526001600160a01b0386169063ecd0596190602401602060405180830381865afa158015610e3b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e5f91906121fc565b610e7f576040516369c9a24560e11b8152600160048201526024016105fb565b610ea97f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0086611594565b6040516306d61fe760e41b81526001600160a01b03861690636d61fe7090610ed7908790879060040161221b565b5f604051808303815f87803b158015610eee575f5ffd5b505af1158015610f00573d5f5f3e3d5ffd5b505050505050505050565b826004610f18828261150d565b60405163ecd0596160e01b81526004808201526001600160a01b0386169063ecd0596190602401602060405180830381865afa158015610f5a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f7e91906121fc565b610f9d576040516369c9a24560e11b81526004818101526024016105fb565b5f610fcf7f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f03546001600160a01b031690565b9050806001600160a01b0381161561101f576040517f741cbe030000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016105fb565b507f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f03805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0388161790556040516306d61fe760e41b81526001600160a01b03871690636d61fe7090611095908890889060040161221b565b5f604051808303815f87803b1580156110ac575f5ffd5b505af11580156110be573d5f5f3e3d5ffd5b50505050505050505050565b60605f6110d885858561168e565b90969095509350505050565b8260026110f1828261150d565b60405163ecd0596160e01b8152600260048201526001600160a01b0386169063ecd0596190602401602060405180830381865afa158015611134573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061115891906121fc565b611178576040516369c9a24560e11b8152600260048201526024016105fb565b610ea97f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0186611594565b8260036111af828261150d565b60405163ecd0596160e01b8152600360048201526001600160a01b0386169063ecd0596190602401602060405180830381865afa1580156111f2573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061121691906121fc565b611236576040516369c9a24560e11b8152600360048201526024016105fb565b5f611244600482868861222e565b61124d91612255565b90505f8585600481811061126357611263611eca565b9050013560f81c60f81b905061127c815f60f81b610cc0565b80611290575061129081607f60f91b610cc0565b6112c6576040517f867a1dcf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6112d4866005818a61222e565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152509293505050506001600160e01b031983166306d61fe760e41b148061135257506001600160e01b031983167f8a91b0e300000000000000000000000000000000000000000000000000000000145b8061136557506001600160e01b03198316155b1561139c576040517fc001660b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160e01b031983165f9081527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f02602052604090205483906001600160a01b031615611423576040517fa56a04dd0000000000000000000000000000000000000000000000000000000081526001600160e01b031990911660048201526024016105fb565b506040805180820182526001600160a01b038a81168083526001600160f81b0319861660208085019182526001600160e01b031989165f9081527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0290915285902093518454915160f81c600160a01b027fffffffffffffffffffffff0000000000000000000000000000000000000000009092169316929092179190911790915590516306d61fe760e41b8152636d61fe70906114e49084906004016119d8565b5f604051808303815f87803b1580156114fb575f5ffd5b505af1158015610c84573d5f5f3e3d5ffd5b5f546001600160a01b0316801561158f576040517f96fb72170000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152602482018490528216906396fb7217906044015f6040518083038186803b15801561157d575f5ffd5b505afa1580156106c9573d5f5f3e3d5ffd5b505050565b6001600160a01b03811615806115b357506001600160a01b0381166001145b156115dc57604051637c84ecfb60e01b81526001600160a01b03821660048201526024016105fb565b6001600160a01b038181165f908152602084905260409020541615611638576040517f40d3d1a40000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016105fb565b60015f818152602093909352604080842080546001600160a01b0394851680875292862080549590911673ffffffffffffffffffffffffffffffffffffffff199586161790559190935280549091169091179055565b60605f6001600160a01b0384166001148015906116b257506116b0858561184f565b155b156116db57604051637c84ecfb60e01b81526001600160a01b03851660048201526024016105fb565b825f03611714576040517ff725081700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8267ffffffffffffffff81111561172d5761172d6118e3565b604051908082528060200260200182016040528015611756578160200160208202803683370190505b506001600160a01b038086165f90815260208890526040812054929450911691505b6001600160a01b0382161580159061179a57506001600160a01b038216600114155b80156117a557508381105b156117fe57818382815181106117bd576117bd611eca565b6001600160a01b039283166020918202929092018101919091529281165f9081529287905260409092205490911690806117f68161229f565b915050611778565b6001600160a01b03821660011480159061181757505f81115b1561184357826118286001836122b7565b8151811061183857611838611eca565b602002602001015191505b80835250935093915050565b5f60016001600160a01b0383161480159061188257506001600160a01b038281165f908152602085905260409020541615155b9392505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b6001600160a01b0385168152836020820152606060408201525f6118d9606083018486611889565b9695505050505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215611907575f5ffd5b815167ffffffffffffffff81111561191d575f5ffd5b8201601f8101841361192d575f5ffd5b805167ffffffffffffffff811115611947576119476118e3565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715611976576119766118e3565b60405281815282820160200186101561198d575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61188260208301846119aa565b6001600160a01b03811681146119fe575f5ffd5b50565b8035611a0c816119ea565b919050565b5f5f83601f840112611a21575f5ffd5b50813567ffffffffffffffff811115611a38575f5ffd5b6020830191508360208260051b8501011115611a52575f5ffd5b9250929050565b803560ff81168114611a0c575f5ffd5b5f5f5f5f5f5f5f60a0888a031215611a7f575f5ffd5b8735611a8a816119ea565b9650602088013567ffffffffffffffff811115611aa5575f5ffd5b8801601f81018a13611ab5575f5ffd5b803567ffffffffffffffff811115611acb575f5ffd5b8a6020828401011115611adc575f5ffd5b60209190910196509450611af260408901611a01565b9350606088013567ffffffffffffffff811115611b0d575f5ffd5b611b198a828b01611a11565b9094509250611b2c905060808901611a59565b905092959891949750929550565b5f60208284031215611b4a575f5ffd5b81356001600160e01b031981168114611882575f5ffd5b5f60408284031215611b71575f5ffd5b50919050565b5f5f5f5f5f5f5f60a0888a031215611b8d575f5ffd5b873567ffffffffffffffff811115611ba3575f5ffd5b611baf8a828b01611a11565b909850965050602088013567ffffffffffffffff811115611bce575f5ffd5b611bda8a828b01611b61565b9550506040880135611af2816119ea565b5f5f60408385031215611bfc575f5ffd5b8235611c07816119ea565b946020939093013593505050565b604080825283519082018190525f9060208501906060840190835b81811015611c575783516001600160a01b0316835260209384019390920191600101611c30565b505080925050506001600160a01b03831660208301529392505050565b5f5f5f5f5f60808688031215611c88575f5ffd5b853567ffffffffffffffff811115611c9e575f5ffd5b611caa88828901611b61565b9550506020860135611cbb816119ea565b9350604086013567ffffffffffffffff811115611cd6575f5ffd5b611ce288828901611a11565b9094509250611cf5905060608701611a59565b90509295509295909350565b6001600160f81b03198816815260e060208201525f611d2360e08301896119aa565b8281036040840152611d3581896119aa565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015611d8a578351835260209384019390920191600101611d6c565b50909b9a5050505050505050505050565b5f5f5f5f5f5f5f5f5f5f5f60e08c8e031215611db5575f5ffd5b8b3567ffffffffffffffff811115611dcb575f5ffd5b611dd78e828f01611a11565b909c509a505060208c013567ffffffffffffffff811115611df6575f5ffd5b611e028e828f01611a11565b909a5098505060408c013567ffffffffffffffff811115611e21575f5ffd5b611e2d8e828f01611b61565b97505060608c013567ffffffffffffffff811115611e49575f5ffd5b611e558e828f01611a11565b9097509550611e68905060808d01611a01565b935060a08c013567ffffffffffffffff811115611e83575f5ffd5b611e8f8e828f01611a11565b9094509250611ea2905060c08d01611a59565b90509295989b509295989b9093969950565b5f82518060208501845e5f920191825250919050565b634e487b7160e01b5f52603260045260245ffd5b5f8235603e19833603018112611ef2575f5ffd5b9190910192915050565b5f60208284031215611f0c575f5ffd5b8135611882816119ea565b5f5f8335601e19843603018112611f2c575f5ffd5b83018035915067ffffffffffffffff821115611f46575f5ffd5b602001915036819003821315611a52575f5ffd5b8183526020830192505f815f5b84811015611f98578135611f7a816119ea565b6001600160a01b031686526020958601959190910190600101611f67565b5093949350505050565b6001600160a01b038816815260a060208201525f611fc460a08301888a611889565b6001600160a01b03871660408401528281036060840152611fe6818688611f5a565b91505060ff8316608083015298975050505050505050565b6001600160a01b0383168152604060208201525f61201f60408301846119aa565b949350505050565b5f8135612033816119ea565b6001600160a01b03168352602082013536839003601e19018112612055575f5ffd5b820160208101903567ffffffffffffffff811115612071575f5ffd5b80360382131561207f575f5ffd5b60406020860152612094604086018284611889565b95945050505050565b5f8383855260208501945060208460051b820101835f5b868110156120fc57838303601f19018852813536879003603e190181126120d9575f5ffd5b6120e584888301612027565b6020998a01999094509290920191506001016120b4565b50909695505050505050565b60a081525f61211b60a08301898b61209d565b828103602084015261212d8189612027565b90506001600160a01b03871660408401528281036060840152611fe6818688611f5a565b60e081525f61216460e083018d8f61209d565b8281036020840152612177818c8e61209d565b9050828103604084015261218b818b612027565b905082810360608401526121a081898b61209d565b90506001600160a01b038716608084015282810360a08401526121c4818688611f5a565b91505060ff831660c08301529c9b505050505050505050505050565b60ff84168152604060208201525f612094604083018486611f5a565b5f6020828403121561220c575f5ffd5b81518015158114611882575f5ffd5b602081525f61201f602083018486611889565b5f5f8585111561223c575f5ffd5b83861115612248575f5ffd5b5050820193919092039150565b80356001600160e01b03198116906004841015612284576001600160e01b0319808560040360031b1b82161691505b5092915050565b634e487b7160e01b5f52601160045260245ffd5b5f600182016122b0576122b061228b565b5060010190565b81810381811115610cd157610cd161228b56fea164736f6c634300081b000a
Deployed Bytecode
0x6080604052600436106100ca575f3560e01c8063837b892e116100735780639e2533ed1161004d5780639e2533ed14610425578063d6fe71f114610444578063ea5f61d014610463576100d1565b8063837b892e146103b357806384b0196e146103df57806385a924cc14610406576100d1565b806355470cf1116100a457806355470cf1146103495780635faac46b146103685780637b10399914610395576100d1565b806301fe9ff2146102455780630a664dba14610266578063481ddd2314610297576100d1565b366100d157005b5f3660605f6101077f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f03546001600160a01b031690565b90506001600160a01b038116610128576101218484610482565b9150610239565b6040517fd68f60250000000000000000000000000000000000000000000000000000000081525f906001600160a01b0383169063d68f6025906101759033903490869036906004016118b1565b5f604051808303815f875af1158015610190573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526101b791908101906118f7565b90506101c38585610482565b92506040517f173bf7da0000000000000000000000000000000000000000000000000000000081526001600160a01b0383169063173bf7da9061020a9084906004016119d8565b5f604051808303815f87803b158015610221575f5ffd5b505af1158015610233573d5f5f3e3d5ffd5b50505050505b50915050805190602001f35b348015610250575f5ffd5b5061026461025f366004611a69565b6106b2565b005b348015610271575f5ffd5b5061027a6106d2565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102a2575f5ffd5b506103216102b1366004611b3a565b6001600160e01b0319165f9081527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260209081526040918290208251808401909352546001600160a01b038116808452600160a01b90910460f81b6001600160f81b031916929091018290529091565b604080516001600160f81b031990931683526001600160a01b0390911660208301520161028e565b348015610354575f5ffd5b50610264610363366004611b77565b610709565b348015610373575f5ffd5b50610387610382366004611beb565b6107d2565b60405161028e929190611c15565b3480156103a0575f5ffd5b505f5461027a906001600160a01b031681565b3480156103be575f5ffd5b506103d26103cd366004611c74565b61080b565b60405161028e91906119d8565b3480156103ea575f5ffd5b506103f36108a0565b60405161028e9796959493929190611d01565b348015610411575f5ffd5b506103d2610420366004611b77565b610948565b348015610430575f5ffd5b506103d261043f366004611d9b565b6109d1565b34801561044f575f5ffd5b5061026461045e366004611d9b565b610a66565b34801561046e575f5ffd5b5061038761047d366004611beb565b610c92565b5f80356001600160e01b03191681527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0260205260408120805460609291906001600160a01b03811690600160a01b900460f81b8115610616576104e981607f60f91b610cc0565b1561055657816001600160a01b03166105028888610cd7565b60405161050f9190611eb4565b5f60405180830381855afa9150503d805f8114610547576040519150601f19603f3d011682016040523d82523d5f602084013e61054c565b606091505b5095509350610604565b610560815f610cc0565b156105c157816001600160a01b03163461057a8989610cd7565b6040516105879190611eb4565b5f6040518083038185875af1925050503d805f8114610547576040519150601f19603f3d011682016040523d82523d5f602084013e61054c565b6040517fb96fcfe40000000000000000000000000000000000000000000000000000000081526001600160f81b0319821660048201526024015b60405180910390fd5b8361061157845160208601fd5b6106a8565b5f3560e01c63150b7a02811463f23a6e61821463bc197c81831417171561065557600194506040519550600486528060e01b6020870152602486016040525b6001600160e01b03195f3516856106a5576040517f08c63e270000000000000000000000000000000000000000000000000000000081526001600160e01b031990911660048201526024016105fb565b50505b5050505092915050565b6106be84848484610d0c565b6106c9878787610deb565b50505050505050565b5f6107047f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f03546001600160a01b031690565b905090565b61071584848484610d0c565b5f5b868110156107935761078b88888381811061073457610734611eca565b90506020028101906107469190611ede565b610754906020810190611efc565b89898481811061076657610766611eca565b90506020028101906107789190611ede565b610786906020810190611f17565b610deb565b600101610717565b505f6107a26020870187611efc565b6001600160a01b0316146106c9576106c96107c06020870187611efc565b6107cd6020880188611f17565b610f0b565b60605f6108007f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0085856110ca565b909590945092505050565b606030806301fe9ff261082160208a018a611efc565b61082e60208b018b611f17565b8a8a8a8a6040516024016108489796959493929190611fa2565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050604051602001610886929190611ffe565b604051602081830303815290604052905095945050505050565b7f0f000000000000000000000000000000000000000000000000000000000000006060805f808083610936604080518082018252600e81527f4e65787573426f6f7473747261700000000000000000000000000000000000006020808301919091528251808401909352600583527f312e302e300000000000000000000000000000000000000000000000000000009083015291565b97989097965046955030945091925090565b606030306001600160a01b03166355470cf18a8a8a8a8a8a8a6040516024016109779796959493929190612108565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506040516020016109b5929190611ffe565b6040516020818303038152906040529050979650505050505050565b606030306001600160a01b031663d6fe71f18e8e8e8e8e8e8e8e8e8e8e604051602401610a089b9a99989796959493929190612151565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050604051602001610a46929190611ffe565b60405160208183030381529060405290509b9a5050505050505050505050565b610a7284848484610d0c565b5f5b8a811015610acb57610ac38c8c83818110610a9157610a91611eca565b9050602002810190610aa39190611ede565b610ab1906020810190611efc565b8d8d8481811061076657610766611eca565b600101610a74565b505f5b88811015610b8b575f8a8a83818110610ae957610ae9611eca565b9050602002810190610afb9190611ede565b610b09906020810190611efc565b6001600160a01b031614610b8357610b838a8a83818110610b2c57610b2c611eca565b9050602002810190610b3e9190611ede565b610b4c906020810190611efc565b8b8b84818110610b5e57610b5e611eca565b9050602002810190610b709190611ede565b610b7e906020810190611f17565b6110e4565b600101610ace565b505f610b9a6020890189611efc565b6001600160a01b031614610bc557610bc5610bb86020890189611efc565b6107cd60208a018a611f17565b5f5b85811015610c84575f878783818110610be257610be2611eca565b9050602002810190610bf49190611ede565b610c02906020810190611efc565b6001600160a01b031614610c7c57610c7c878783818110610c2557610c25611eca565b9050602002810190610c379190611ede565b610c45906020810190611efc565b888884818110610c5757610c57611eca565b9050602002810190610c699190611ede565b610c77906020810190611f17565b6111a2565b600101610bc7565b505050505050505050505050565b60605f6108007f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0185856110ca565b6001600160f81b0319828116908216145b92915050565b6060604080513681016020019091529050601436018152365f602083013760408051601481019091523360601b905292915050565b5f805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03861690811790915515610db2576040517ff05c04e10000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063f05c04e190610d84908490879087906004016121e0565b5f604051808303815f87803b158015610d9b575f5ffd5b505af1158015610dad573d5f5f3e3d5ffd5b505050505b6040516001600160a01b038516907ff98c8404c5b1bfef2e6ba9233c6e88845aedfd36eea8b192725d8c199571cf32905f90a250505050565b826001610df8828261150d565b60405163ecd0596160e01b8152600160048201526001600160a01b0386169063ecd0596190602401602060405180830381865afa158015610e3b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e5f91906121fc565b610e7f576040516369c9a24560e11b8152600160048201526024016105fb565b610ea97f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0086611594565b6040516306d61fe760e41b81526001600160a01b03861690636d61fe7090610ed7908790879060040161221b565b5f604051808303815f87803b158015610eee575f5ffd5b505af1158015610f00573d5f5f3e3d5ffd5b505050505050505050565b826004610f18828261150d565b60405163ecd0596160e01b81526004808201526001600160a01b0386169063ecd0596190602401602060405180830381865afa158015610f5a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f7e91906121fc565b610f9d576040516369c9a24560e11b81526004818101526024016105fb565b5f610fcf7f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f03546001600160a01b031690565b9050806001600160a01b0381161561101f576040517f741cbe030000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024016105fb565b507f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f03805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0388161790556040516306d61fe760e41b81526001600160a01b03871690636d61fe7090611095908890889060040161221b565b5f604051808303815f87803b1580156110ac575f5ffd5b505af11580156110be573d5f5f3e3d5ffd5b50505050505050505050565b60605f6110d885858561168e565b90969095509350505050565b8260026110f1828261150d565b60405163ecd0596160e01b8152600260048201526001600160a01b0386169063ecd0596190602401602060405180830381865afa158015611134573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061115891906121fc565b611178576040516369c9a24560e11b8152600260048201526024016105fb565b610ea97f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0186611594565b8260036111af828261150d565b60405163ecd0596160e01b8152600360048201526001600160a01b0386169063ecd0596190602401602060405180830381865afa1580156111f2573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061121691906121fc565b611236576040516369c9a24560e11b8152600360048201526024016105fb565b5f611244600482868861222e565b61124d91612255565b90505f8585600481811061126357611263611eca565b9050013560f81c60f81b905061127c815f60f81b610cc0565b80611290575061129081607f60f91b610cc0565b6112c6576040517f867a1dcf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6112d4866005818a61222e565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152509293505050506001600160e01b031983166306d61fe760e41b148061135257506001600160e01b031983167f8a91b0e300000000000000000000000000000000000000000000000000000000145b8061136557506001600160e01b03198316155b1561139c576040517fc001660b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160e01b031983165f9081527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f02602052604090205483906001600160a01b031615611423576040517fa56a04dd0000000000000000000000000000000000000000000000000000000081526001600160e01b031990911660048201526024016105fb565b506040805180820182526001600160a01b038a81168083526001600160f81b0319861660208085019182526001600160e01b031989165f9081527f0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f0290915285902093518454915160f81c600160a01b027fffffffffffffffffffffff0000000000000000000000000000000000000000009092169316929092179190911790915590516306d61fe760e41b8152636d61fe70906114e49084906004016119d8565b5f604051808303815f87803b1580156114fb575f5ffd5b505af1158015610c84573d5f5f3e3d5ffd5b5f546001600160a01b0316801561158f576040517f96fb72170000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152602482018490528216906396fb7217906044015f6040518083038186803b15801561157d575f5ffd5b505afa1580156106c9573d5f5f3e3d5ffd5b505050565b6001600160a01b03811615806115b357506001600160a01b0381166001145b156115dc57604051637c84ecfb60e01b81526001600160a01b03821660048201526024016105fb565b6001600160a01b038181165f908152602084905260409020541615611638576040517f40d3d1a40000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016105fb565b60015f818152602093909352604080842080546001600160a01b0394851680875292862080549590911673ffffffffffffffffffffffffffffffffffffffff199586161790559190935280549091169091179055565b60605f6001600160a01b0384166001148015906116b257506116b0858561184f565b155b156116db57604051637c84ecfb60e01b81526001600160a01b03851660048201526024016105fb565b825f03611714576040517ff725081700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8267ffffffffffffffff81111561172d5761172d6118e3565b604051908082528060200260200182016040528015611756578160200160208202803683370190505b506001600160a01b038086165f90815260208890526040812054929450911691505b6001600160a01b0382161580159061179a57506001600160a01b038216600114155b80156117a557508381105b156117fe57818382815181106117bd576117bd611eca565b6001600160a01b039283166020918202929092018101919091529281165f9081529287905260409092205490911690806117f68161229f565b915050611778565b6001600160a01b03821660011480159061181757505f81115b1561184357826118286001836122b7565b8151811061183857611838611eca565b602002602001015191505b80835250935093915050565b5f60016001600160a01b0383161480159061188257506001600160a01b038281165f908152602085905260409020541615155b9392505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b6001600160a01b0385168152836020820152606060408201525f6118d9606083018486611889565b9695505050505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215611907575f5ffd5b815167ffffffffffffffff81111561191d575f5ffd5b8201601f8101841361192d575f5ffd5b805167ffffffffffffffff811115611947576119476118e3565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715611976576119766118e3565b60405281815282820160200186101561198d575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61188260208301846119aa565b6001600160a01b03811681146119fe575f5ffd5b50565b8035611a0c816119ea565b919050565b5f5f83601f840112611a21575f5ffd5b50813567ffffffffffffffff811115611a38575f5ffd5b6020830191508360208260051b8501011115611a52575f5ffd5b9250929050565b803560ff81168114611a0c575f5ffd5b5f5f5f5f5f5f5f60a0888a031215611a7f575f5ffd5b8735611a8a816119ea565b9650602088013567ffffffffffffffff811115611aa5575f5ffd5b8801601f81018a13611ab5575f5ffd5b803567ffffffffffffffff811115611acb575f5ffd5b8a6020828401011115611adc575f5ffd5b60209190910196509450611af260408901611a01565b9350606088013567ffffffffffffffff811115611b0d575f5ffd5b611b198a828b01611a11565b9094509250611b2c905060808901611a59565b905092959891949750929550565b5f60208284031215611b4a575f5ffd5b81356001600160e01b031981168114611882575f5ffd5b5f60408284031215611b71575f5ffd5b50919050565b5f5f5f5f5f5f5f60a0888a031215611b8d575f5ffd5b873567ffffffffffffffff811115611ba3575f5ffd5b611baf8a828b01611a11565b909850965050602088013567ffffffffffffffff811115611bce575f5ffd5b611bda8a828b01611b61565b9550506040880135611af2816119ea565b5f5f60408385031215611bfc575f5ffd5b8235611c07816119ea565b946020939093013593505050565b604080825283519082018190525f9060208501906060840190835b81811015611c575783516001600160a01b0316835260209384019390920191600101611c30565b505080925050506001600160a01b03831660208301529392505050565b5f5f5f5f5f60808688031215611c88575f5ffd5b853567ffffffffffffffff811115611c9e575f5ffd5b611caa88828901611b61565b9550506020860135611cbb816119ea565b9350604086013567ffffffffffffffff811115611cd6575f5ffd5b611ce288828901611a11565b9094509250611cf5905060608701611a59565b90509295509295909350565b6001600160f81b03198816815260e060208201525f611d2360e08301896119aa565b8281036040840152611d3581896119aa565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015611d8a578351835260209384019390920191600101611d6c565b50909b9a5050505050505050505050565b5f5f5f5f5f5f5f5f5f5f5f60e08c8e031215611db5575f5ffd5b8b3567ffffffffffffffff811115611dcb575f5ffd5b611dd78e828f01611a11565b909c509a505060208c013567ffffffffffffffff811115611df6575f5ffd5b611e028e828f01611a11565b909a5098505060408c013567ffffffffffffffff811115611e21575f5ffd5b611e2d8e828f01611b61565b97505060608c013567ffffffffffffffff811115611e49575f5ffd5b611e558e828f01611a11565b9097509550611e68905060808d01611a01565b935060a08c013567ffffffffffffffff811115611e83575f5ffd5b611e8f8e828f01611a11565b9094509250611ea2905060c08d01611a59565b90509295989b509295989b9093969950565b5f82518060208501845e5f920191825250919050565b634e487b7160e01b5f52603260045260245ffd5b5f8235603e19833603018112611ef2575f5ffd5b9190910192915050565b5f60208284031215611f0c575f5ffd5b8135611882816119ea565b5f5f8335601e19843603018112611f2c575f5ffd5b83018035915067ffffffffffffffff821115611f46575f5ffd5b602001915036819003821315611a52575f5ffd5b8183526020830192505f815f5b84811015611f98578135611f7a816119ea565b6001600160a01b031686526020958601959190910190600101611f67565b5093949350505050565b6001600160a01b038816815260a060208201525f611fc460a08301888a611889565b6001600160a01b03871660408401528281036060840152611fe6818688611f5a565b91505060ff8316608083015298975050505050505050565b6001600160a01b0383168152604060208201525f61201f60408301846119aa565b949350505050565b5f8135612033816119ea565b6001600160a01b03168352602082013536839003601e19018112612055575f5ffd5b820160208101903567ffffffffffffffff811115612071575f5ffd5b80360382131561207f575f5ffd5b60406020860152612094604086018284611889565b95945050505050565b5f8383855260208501945060208460051b820101835f5b868110156120fc57838303601f19018852813536879003603e190181126120d9575f5ffd5b6120e584888301612027565b6020998a01999094509290920191506001016120b4565b50909695505050505050565b60a081525f61211b60a08301898b61209d565b828103602084015261212d8189612027565b90506001600160a01b03871660408401528281036060840152611fe6818688611f5a565b60e081525f61216460e083018d8f61209d565b8281036020840152612177818c8e61209d565b9050828103604084015261218b818b612027565b905082810360608401526121a081898b61209d565b90506001600160a01b038716608084015282810360a08401526121c4818688611f5a565b91505060ff831660c08301529c9b505050505050505050505050565b60ff84168152604060208201525f612094604083018486611f5a565b5f6020828403121561220c575f5ffd5b81518015158114611882575f5ffd5b602081525f61201f602083018486611889565b5f5f8585111561223c575f5ffd5b83861115612248575f5ffd5b5050820193919092039150565b80356001600160e01b03198116906004841015612284576001600160e01b0319808560040360031b1b82161691505b5092915050565b634e487b7160e01b5f52601160045260245ffd5b5f600182016122b0576122b061228b565b5060010190565b81810381811115610cd157610cd161228b56fea164736f6c634300081b000a
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.