Overview
ETH Balance
0.0998766 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Transfer | 24056745 | 37 days ago | IN | 0.1 ETH | 0.00000004951 |
Loading...
Loading
Minimal Proxy Contract for 0x186b91ae45dd22def329bf6b4233cf910e157c84
Contract Name:
Elytro
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 100000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {IAccount, PackedUserOperation} from "@soulwallet-core/contracts/interface/IAccount.sol"; import {EntryPointManager} from "@soulwallet-core/contracts/base/EntryPointManager.sol"; import {FallbackManager} from "@soulwallet-core/contracts/base/FallbackManager.sol"; import {StandardExecutor} from "@soulwallet-core/contracts/base/StandardExecutor.sol"; import {ValidatorManager} from "@soulwallet-core/contracts/base/ValidatorManager.sol"; import {SignatureDecoder} from "@soulwallet-core/contracts/utils/SignatureDecoder.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import {Errors} from "./libraries/Errors.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "./abstract/ERC1271Handler.sol"; import {ElytroOwnerManager} from "./abstract/ElytroOwnerManager.sol"; import {ElytroModuleManager} from "./abstract/ElytroModuleManager.sol"; import {ElytroHookManager} from "./abstract/ElytroHookManager.sol"; import {ElytroUpgradeManager} from "./abstract/ElytroUpgradeManager.sol"; /** * @title Elytro * @dev This contract is the main entry point for the Elytro. It implements the IAccount and IERC1271 interfaces, * and is compatible with the ERC-4337 standard. * It inherits from multiple base contracts and managers to provide the core functionality of the wallet. * This includes managing entry points, owners, modules, hooks, and upgrades, as well as handling ERC1271 signatures and providing a fallback function. */ contract Elytro is Initializable, IAccount, IERC1271, EntryPointManager, ElytroOwnerManager, ElytroModuleManager, ElytroHookManager, StandardExecutor, ValidatorManager, FallbackManager, ElytroUpgradeManager, ERC1271Handler { string public constant VERSION = "1.1.0"; address internal immutable _DEFAULT_VALIDATOR; constructor(address _entryPoint, address defaultValidator) EntryPointManager(_entryPoint) { _DEFAULT_VALIDATOR = defaultValidator; _disableInitializers(); } /** * @notice Initializes the Elytro contract * @dev This function can only be called once. It sets the initial owners, default callback handler, modules, and hooks. */ function initialize( bytes32[] calldata owners, address defalutCallbackHandler, bytes[] calldata modules, bytes[] calldata hooks ) external initializer { _addOwners(owners); _setFallbackHandler(defalutCallbackHandler); _installValidator(_DEFAULT_VALIDATOR, hex""); for (uint256 i = 0; i < modules.length;) { _addModule(modules[i]); unchecked { i++; } } for (uint256 i = 0; i < hooks.length;) { _installHook(hooks[i]); unchecked { i++; } } } function _uninstallValidator(address validator) internal override { require(validator != _DEFAULT_VALIDATOR, "can't uninstall default validator"); super._uninstallValidator(validator); } function isValidSignature(bytes32 _hash, bytes calldata signature) public view override returns (bytes4 magicValue) { bytes32 datahash = _encodeRawHash(_hash); (address validator, bytes calldata validatorSignature, bytes calldata hookSignature) = SignatureDecoder.signatureSplit(signature); _preIsValidSignatureHook(datahash, hookSignature); return _isValidSignature(datahash, validator, validatorSignature); } function _decodeSignature(bytes calldata signature) internal pure virtual returns (address validator, bytes calldata validatorSignature, bytes calldata hookSignature) { return SignatureDecoder.signatureSplit(signature); } function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) public payable virtual override returns (uint256 validationData) { _onlyEntryPoint(); assembly ("memory-safe") { if missingAccountFunds { // ignore failure (its EntryPoint's job to verify, not account.) pop(call(gas(), caller(), missingAccountFunds, 0x00, 0x00, 0x00, 0x00)) } } (address validator, bytes calldata validatorSignature, bytes calldata hookSignature) = _decodeSignature(userOp.signature); /* Warning!!! This function uses `return` to terminate the execution of the entire contract. If any `Hook` fails, this function will stop the contract's execution and return `SIG_VALIDATION_FAILED`, skipping all the subsequent unexecuted code. */ _preUserOpValidationHook(userOp, userOpHash, missingAccountFunds, hookSignature); /* When any hook execution fails, this line will not be executed. */ return _validateUserOp(userOp, userOpHash, validator, validatorSignature); } /* The permission to upgrade the logic contract is exclusively granted to modules (UpgradeModule), meaning that even the wallet owner cannot directly invoke `upgradeTo` for upgrades. This design is implemented for security reasons, ensuring that even if the signer's credentials are compromised, attackers cannot upgrade the logic contract, potentially rendering the wallet unusable. Users can regain control over their wallet through social recovery mechanisms. This approach safeguards the wallet's integrity, maintaining its availability and security. */ function upgradeTo(address newImplementation) external override { _onlyModule(); _upgradeTo(newImplementation); } /// @notice Handles the upgrade from an old implementation /// @param oldImplementation Address of the old implementation function upgradeFrom(address oldImplementation) external pure override { (oldImplementation); revert Errors.NOT_IMPLEMENTED(); //Initial version no need data migration } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; // refer: https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/interfaces/IAccount.sol import {PackedUserOperation} from "@account-abstraction/contracts/interfaces/PackedUserOperation.sol"; interface IAccount { /** * Validate user's signature and nonce * the entryPoint will make the call to the recipient only if this validation call returns successfully. * signature failure should be reported by returning SIG_VALIDATION_FAILED (1). * This allows making a "simulation call" without a valid signature * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure. * * @dev Must validate caller is the entryPoint. * Must validate the signature and nonce * @param userOp - The operation that is about to be executed. * @param userOpHash - Hash of the user's request data. can be used as the basis for signature. * @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint. * This is the minimum amount to transfer to the sender(entryPoint) to be * able to make the call. The excess is left as a deposit in the entrypoint * for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()". * In case there is a paymaster in the request (or the current deposit is high * enough), this value will be zero. * @return validationData - Packaged ValidationData structure. use `_packValidationData` and * `_unpackValidationData` to encode and decode. * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, * otherwise, an address of an "authorizer" contract. * <6-byte> validUntil - Last timestamp this operation is valid. 0 for "indefinite" * <6-byte> validAfter - First timestamp this operation is valid * If an account doesn't use time-range, it is enough to * return SIG_VALIDATION_FAILED value (1) for signature failure. * Note that the validation code cannot use block.timestamp (or block.number) directly. */ function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) external payable returns (uint256 validationData); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {Authority} from "./Authority.sol"; abstract contract EntryPointManager is Authority { /** * @dev use immutable to save gas */ address internal immutable _ENTRY_POINT; /** * a custom error for caller must be entry point */ error CALLER_MUST_BE_ENTRY_POINT(); constructor(address _entryPoint) { _ENTRY_POINT = _entryPoint; } function entryPoint() external view returns (address) { return _ENTRY_POINT; } /** * @notice Ensures the calling contract is the entrypoint */ function _onlyEntryPoint() internal view override { if (msg.sender != _ENTRY_POINT) { revert CALLER_MUST_BE_ENTRY_POINT(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {Authority} from "./Authority.sol"; import {IFallbackManager} from "../interface/IFallbackManager.sol"; import {AccountStorage} from "../utils/AccountStorage.sol"; import {FallbackManagerSnippet} from "../snippets/FallbackManager.sol"; abstract contract FallbackManager is IFallbackManager, Authority, FallbackManagerSnippet { receive() external payable virtual {} /** * @dev Sets the address of the fallback handler contract * @param fallbackContract The address of the new fallback handler contract */ function _setFallbackHandler(address fallbackContract) internal virtual override { AccountStorage.layout().defaultFallbackContract = fallbackContract; } /** * @notice Fallback function that forwards all requests to the fallback handler contract * @dev The request is forwarded using a STATICCALL * It ensures that the state of the contract doesn't change even if the fallback function has state-changing operations */ fallback() external payable virtual { address fallbackContract = AccountStorage.layout().defaultFallbackContract; assembly ("memory-safe") { function allocate(length) -> pos { pos := mload(0x40) mstore(0x40, add(pos, length)) } if iszero(fallbackContract) { return(0, 0) } let calldataPtr := allocate(calldatasize()) calldatacopy(calldataPtr, 0, calldatasize()) let result := staticcall(gas(), fallbackContract, calldataPtr, calldatasize(), 0, 0) let returndataPtr := allocate(returndatasize()) returndatacopy(returndataPtr, 0, returndatasize()) if iszero(result) { revert(returndataPtr, returndatasize()) } return(returndataPtr, returndatasize()) } } /** * @notice Sets the address of the fallback handler and emits the FallbackChanged event * @param fallbackContract The address of the new fallback handler */ function setFallbackHandler(address fallbackContract) external virtual override { fallbackManagementAccess(); _setFallbackHandler(fallbackContract); emit FallbackChanged(fallbackContract); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {Authority} from "./Authority.sol"; import {IStandardExecutor, Execution} from "../interface/IStandardExecutor.sol"; import {EntryPointManager} from "./EntryPointManager.sol"; abstract contract StandardExecutor is Authority, IStandardExecutor, EntryPointManager { /** * @dev execute method * only entrypoint can call this method * @param target the target address * @param value the value * @param data the data */ function execute(address target, uint256 value, bytes calldata data) external payable virtual override { executorAccess(); assembly ("memory-safe") { // memorySafe: Memory allocated by yourself using a mechanism like the allocate function described above. function allocate(length) -> pos { pos := mload(0x40) mstore(0x40, add(pos, length)) } let calldataPtr := allocate(data.length) calldatacopy(calldataPtr, data.offset, data.length) let result := call(gas(), target, value, calldataPtr, data.length, 0, 0) // note: return data is ignored if iszero(result) { let returndataPtr := allocate(returndatasize()) returndatacopy(returndataPtr, 0, returndatasize()) revert(returndataPtr, returndatasize()) } } } /** * @dev execute batch method * only entrypoint can call this method * @param executions the executions */ function executeBatch(Execution[] calldata executions) external payable virtual override { executorAccess(); for (uint256 i = 0; i < executions.length; i++) { Execution calldata execution = executions[i]; address target = execution.target; uint256 value = execution.value; bytes calldata data = execution.data; assembly ("memory-safe") { // memorySafe: Memory allocated by yourself using a mechanism like the allocate function described above. function allocate(length) -> pos { pos := mload(0x40) mstore(0x40, add(pos, length)) } let calldataPtr := allocate(data.length) calldatacopy(calldataPtr, data.offset, data.length) let result := call(gas(), target, value, calldataPtr, data.length, 0, 0) // note: return data is ignored if iszero(result) { let returndataPtr := allocate(returndatasize()) returndatacopy(returndataPtr, 0, returndatasize()) revert(returndataPtr, returndatasize()) } } } } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; import {Authority} from "./Authority.sol"; import {IValidatorManager} from "../interface/IValidatorManager.sol"; import {IValidator} from "../interface/IValidator.sol"; import {PackedUserOperation} from "../interface/IAccount.sol"; import {AccountStorage} from "../utils/AccountStorage.sol"; import {AddressLinkedList} from "../utils/AddressLinkedList.sol"; import {SIG_VALIDATION_FAILED} from "../utils/Constants.sol"; import {ValidatorManagerSnippet} from "../snippets/ValidatorManager.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IPluggable} from "../interface/IPluggable.sol"; import {CallDataPack} from "../utils/CalldataPack.sol"; abstract contract ValidatorManager is Authority, IValidatorManager, ValidatorManagerSnippet { using AddressLinkedList for mapping(address => address); error INVALID_VALIDATOR(); error VALIDATOR_ALREADY_EXISTS(); error VALIDATOR_NOT_EXISTS(); bytes4 private constant INTERFACE_ID_VALIDATOR = type(IValidator).interfaceId; /** * @dev checks whether a address is a installed validator */ function _isInstalledValidator(address validator) internal view virtual override returns (bool) { return AccountStorage.layout().validators.isExist(validator); } /** * @dev checks whether a address is a valid validator * note: If you need to extend the interface, override this function * @param validator validator address */ function _isSupportsValidatorInterface(address validator) internal view virtual override returns (bool supported) { bytes memory callData = abi.encodeWithSelector(IERC165.supportsInterface.selector, INTERFACE_ID_VALIDATOR); assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := staticcall(gas(), validator, add(callData, 0x20), mload(callData), 0x00, 0x20) if and(result, eq(returndatasize(), 32)) { supported := mload(0x00) } } } /** * @dev install a validator */ function _installValidator(address validator, bytes memory initData) internal virtual override { if (_isInstalledValidator(validator)) { revert VALIDATOR_ALREADY_EXISTS(); } if (_isSupportsValidatorInterface(validator) == false) { revert INVALID_VALIDATOR(); } AccountStorage.layout().validators.add(validator); bytes memory callData = abi.encodeWithSelector(IPluggable.Init.selector, initData); bytes4 invalidValidatorSelector = INVALID_VALIDATOR.selector; assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := call(gas(), validator, 0, add(callData, 0x20), mload(callData), 0x00, 0x00) if iszero(result) { mstore(0x00, invalidValidatorSelector) revert(0x00, 4) } } emit ValidatorInstalled(validator); } /** * @dev uninstall a validator */ function _uninstallValidator(address validator) internal virtual override { if (!AccountStorage.layout().validators.tryRemove(validator)) { revert VALIDATOR_NOT_EXISTS(); } (bool success,) = validator.call(abi.encodeWithSelector(IPluggable.DeInit.selector)); if (success) { emit ValidatorUninstalled(validator); } else { emit ValidatorUninstalledwithError(validator); } } /** * @dev uninstall a validator */ function uninstallValidator(address validator) external virtual override { validatorManagementAccess(); _uninstallValidator(validator); } /** * @dev list validators */ function listValidator() external view virtual override returns (address[] memory validators) { mapping(address => address) storage validator = AccountStorage.layout().validators; validators = validator.list(AddressLinkedList.SENTINEL_ADDRESS, validator.size()); } /** * @dev EIP-1271 * @param hash hash of the data to be signed * @param validator validator address * @param validatorSignature Signature byte array associated with _data * @return magicValue Magic value 0x1626ba7e if the validator is registered and signature is valid */ function _isValidSignature(bytes32 hash, address validator, bytes calldata validatorSignature) internal view virtual override returns (bytes4 magicValue) { if (_isInstalledValidator(validator) == false) { return bytes4(0); } bytes memory callData = abi.encodeWithSelector(IValidator.validateSignature.selector, msg.sender, hash, validatorSignature); assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := staticcall(gas(), validator, add(callData, 0x20), mload(callData), 0x00, 0x20) /* Since the validator's compliance with the expected interface has been confirmed before, we don't need to handle the scenario where `result=true` but the `returndata` is not returned as expected here. */ if result { magicValue := mload(0x00) } } } /** * @dev validate UserOperation * @param userOp UserOperation * @param userOpHash UserOperation hash * @param validator validator address * @param validatorSignature validator signature * @return validationData refer to https://github.com/eth-infinitism/account-abstraction/blob/v0.6.0/contracts/interfaces/IAccount.sol#L24-L30 */ function _validateUserOp( PackedUserOperation calldata userOp, bytes32 userOpHash, address validator, bytes calldata validatorSignature ) internal virtual override returns (uint256 validationData) { if (_isInstalledValidator(validator) == false) { return SIG_VALIDATION_FAILED; } // abi.encodeWithSelector(IValidator.validateUserOp.selector, userOp, userOpHash, validatorSignature); bytes memory callData = CallDataPack.encodeWithoutUserOpSignature_validateUserOp_UserOperation_bytes32_bytes( userOp, userOpHash, validatorSignature ); assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := call(gas(), validator, 0, add(callData, 0x20), mload(callData), 0x00, 0x20) if iszero(result) { mstore(0x00, SIG_VALIDATION_FAILED) } /* Since the validator's compliance with the expected interface has been confirmed before, we don't need to handle the scenario where `result=true` but the `returndata` is not returned as expected here. */ validationData := mload(0x00) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; library SignatureDecoder { /* Signature: [0:20]: `validator address` [20:24]: n = `validator signature length`, bytes4 max to 16777215 bytes [24:24+n]: `validator signature` [24+n:]: `hook signature` [optional] `hook signature`: [0:20]: `first hook address` [20:24]: n1 = `first hook signature length`, bytes4 max to 16777215 bytes [24:24+n1]: `first hook signature` `[optional]` [24+n1:24+n1+20]: `second hook signature` [24+n1+20:24+n1+24]: n2 = `second hook signature length`, bytes4 max to 16777215 bytes [24+n1+24:24+n1+24+n2]: `second hook signature` ... */ function signatureSplit(bytes calldata self) internal pure returns (address validator, bytes calldata validatorSignature, bytes calldata hookSignature) { /* Equivalent code: validator = address(bytes20(self[0:20])); uint32 validatorSignatureLength = uint32(bytes4(self[20:24])); uint256 hookSignatureStartAt; unchecked { hookSignatureStartAt = 24 + validatorSignatureLength; } validatorSignature = self[24:hookSignatureStartAt]; hookSignature = self[hookSignatureStartAt:]; */ assembly ("memory-safe") { if lt(self.length, 24) { revert(0, 0) } { // validator let _validator := calldataload(self.offset) // _validator >> ((32-20)*8) validator := shr(96, _validator) } { // validatorSignature let _validatorSignatureLength := calldataload(add(20, self.offset)) // _validatorSignatureLength >> ((32-4)*8) let validatorSignatureLength := shr(224, _validatorSignatureLength) if gt(add(24, validatorSignatureLength), self.length) { revert(0, 0) } validatorSignature.offset := add(24, self.offset) validatorSignature.length := validatorSignatureLength } { // hookSignature hookSignature.offset := add(validatorSignature.offset, validatorSignature.length) hookSignature.length := sub(sub(self.length, validatorSignature.length), 24) } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1271.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-1271 standard signature validation method for * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. */ interface IERC1271 { /** * @dev Should return whether the signature provided is valid for the provided data * @param hash Hash of the data to be signed * @param signature Signature byte array associated with _data */ function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.20; library Errors { error ADDRESS_ALREADY_EXISTS(); error ADDRESS_NOT_EXISTS(); error DATA_ALREADY_EXISTS(); error DATA_NOT_EXISTS(); error CALLER_MUST_BE_SELF_OR_MODULE(); error CALLER_MUST_BE_MODULE(); error HASH_ALREADY_APPROVED(); error HASH_ALREADY_REJECTED(); error INVALID_ADDRESS(); error INVALID_SELECTOR(); error INVALID_SIGNTYPE(); error MODULE_SELECTORS_EMPTY(); error MODULE_EXECUTE_FROM_MODULE_RECURSIVE(); error SELECTOR_ALREADY_EXISTS(); error SELECTOR_NOT_EXISTS(); error INVALID_LOGIC_ADDRESS(); error SAME_LOGIC_ADDRESS(); error UPGRADE_FAILED(); error NOT_IMPLEMENTED(); error INVALID_SIGNATURE(); error INVALID_TIME_RANGE(); error UNAUTHORIZED(); error INVALID_DATA(); error GUARDIAN_SIGNATURE_INVALID(); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {Authority} from "@soulwallet-core/contracts/base/Authority.sol"; abstract contract ERC1271Handler is Authority { // Magic value indicating a valid signature for ERC-1271 contracts // bytes4(keccak256("isValidSignature(bytes32,bytes)") bytes4 internal constant MAGICVALUE = 0x1626ba7e; // Constants indicating different invalid states bytes4 internal constant INVALID_ID = 0xffffffff; bytes32 private constant ELYTRO_WALLET_MSG_TYPEHASH = keccak256("ElytroMessage(bytes32 message)"); bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)"); function _encodeRawHash(bytes32 rawHash) internal view returns (bytes32) { bytes32 encode1271MessageHash = keccak256(abi.encode(ELYTRO_WALLET_MSG_TYPEHASH, rawHash)); bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), address(this))); return keccak256(abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator, encode1271MessageHash)); } function getChainId() public view returns (uint256) { uint256 id; // solhint-disable-next-line no-inline-assembly assembly { id := chainid() } return id; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {OwnerManager} from "@soulwallet-core/contracts/base/OwnerManager.sol"; import {IElytroOwnerManager} from "../interfaces/IElytroOwnerManager.sol"; abstract contract ElytroOwnerManager is IElytroOwnerManager, OwnerManager { function _addOwners(bytes32[] calldata owners) internal { for (uint256 i = 0; i < owners.length;) { _addOwner(owners[i]); unchecked { i++; } } } function addOwners(bytes32[] calldata owners) external override { ownerManagementAccess(); _addOwners(owners); } function resetOwners(bytes32[] calldata newOwners) external override { ownerManagementAccess(); _clearOwner(); _addOwners(newOwners); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {ModuleManager} from "@soulwallet-core/contracts/base/ModuleManager.sol"; import {IElytroModuleManager} from "../interfaces/IElytroModuleManager.sol"; import {IElytroModule} from "../modules/interfaces/IElytroModule.sol"; import {Errors} from "../libraries/Errors.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; abstract contract ElytroModuleManager is IElytroModuleManager, ModuleManager { function installModule(bytes calldata moduleAndData) external override { pluginManagementAccess(); _addModule(moduleAndData); } /** * The current function is inside the * `function _installModule(address moduleAddress, bytes memory initData, bytes4[] memory selectors)` */ function _isSupportsModuleInterface(address moduleAddress) internal view override returns (bool supported) { bytes memory callData = abi.encodeWithSelector(IERC165.supportsInterface.selector, type(IElytroModule).interfaceId); assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := staticcall(gas(), moduleAddress, add(callData, 0x20), mload(callData), 0x00, 0x20) if gt(result, 0) { supported := mload(0x00) } } } function _addModule(bytes calldata moduleAndData) internal { address moduleAddress = address(bytes20(moduleAndData[:20])); IElytroModule aModule = IElytroModule(moduleAddress); bytes4[] memory requiredFunctions = aModule.requiredFunctions(); if (requiredFunctions.length == 0) { revert Errors.MODULE_SELECTORS_EMPTY(); } _installModule(address(bytes20(moduleAndData[:20])), moduleAndData[20:], requiredFunctions); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {HookManager} from "@soulwallet-core/contracts/base/HookManager.sol"; import {IElytroHookManager} from "../interfaces/IElytroHookManager.sol"; abstract contract ElytroHookManager is IElytroHookManager, HookManager { function _installHook(bytes calldata hookAndDataWithFlag) internal virtual { _installHook( address(bytes20(hookAndDataWithFlag[:20])), hookAndDataWithFlag[20:hookAndDataWithFlag.length - 1], uint8(bytes1((hookAndDataWithFlag[hookAndDataWithFlag.length - 1:hookAndDataWithFlag.length]))) ); } function installHook(bytes calldata hookAndData, uint8 capabilityFlags) external virtual override { pluginManagementAccess(); _installHook(address(bytes20(hookAndData[:20])), hookAndData[20:], capabilityFlags); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.20; import "../interfaces/IUpgradable.sol"; import "../libraries/Errors.sol"; /** * @title ElytroUpgradeManager * @dev This contract allows for the logic of a proxy to be upgraded */ abstract contract ElytroUpgradeManager is IUpgradable { /** * @dev Storage slot with the address of the current implementation * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1 */ bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Upgrades the logic to a new implementation * @param newImplementation Address of the new implementation */ function _upgradeTo(address newImplementation) internal { bool isContract; assembly ("memory-safe") { isContract := gt(extcodesize(newImplementation), 0) } if (!isContract) { revert Errors.INVALID_LOGIC_ADDRESS(); } address oldImplementation; assembly ("memory-safe") { oldImplementation := and(sload(_IMPLEMENTATION_SLOT), 0xffffffffffffffffffffffffffffffffffffffff) } if (oldImplementation == newImplementation) { revert Errors.SAME_LOGIC_ADDRESS(); } assembly ("memory-safe") { sstore(_IMPLEMENTATION_SLOT, newImplementation) } // delegatecall to new implementation (bool success,) = newImplementation.delegatecall(abi.encodeWithSelector(IUpgradable.upgradeFrom.selector, oldImplementation)); if (!success) { revert Errors.UPGRADE_FAILED(); } emit Upgraded(oldImplementation, newImplementation); } }
// 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.23; import {AuthoritySnippet} from "../snippets/Authority.sol"; abstract contract Authority is AuthoritySnippet { /** * a custom error for caller must be self or module */ error CALLER_MUST_BE_SELF_OR_MODULE(); /** * a custom error for caller must be module */ error CALLER_MUST_BE_MODULE(); /** * @notice Ensures the calling contract is an authorized module */ function _onlyModule() internal view override { if (!_isAuthorizedModule()) { revert CALLER_MUST_BE_MODULE(); } } /** * @notice Ensures the calling contract is either the Authority contract itself or an authorized module * @dev Uses the inherited `_isAuthorizedModule()` from ModuleAuth for module-based authentication */ function _onlySelfOrModule() internal view override { if (msg.sender != address(this) && !_isAuthorizedModule()) { revert CALLER_MUST_BE_SELF_OR_MODULE(); } } /** * @dev Check if access to the following functions: * 1. setFallbackHandler */ function fallbackManagementAccess() internal view virtual override { _onlySelfOrModule(); } /** * @dev Check if access to the following functions: * 1. installHook * 2. uninstallHook * 3. installModule * 4. uninstallModule */ function pluginManagementAccess() internal view virtual override { _onlySelfOrModule(); } /** * @dev Check if access to the following functions: * 1. addOwner * 2. removeOwner * 3. resetOwner */ function ownerManagementAccess() internal view virtual override { _onlySelfOrModule(); } /** * @dev Check if access to the following functions: * 1. execute * 2. executeBatch * 3. executeUserOp */ function executorAccess() internal view virtual override { _onlyEntryPoint(); } /** * @dev Check if access to the following functions: * 1. installValidator * 2. uninstallValidator */ function validatorManagementAccess() internal view virtual override { _onlySelfOrModule(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; interface IFallbackManager { /** * @notice Emitted when the fallback contract is changed * @param fallbackContract The address of the newly set fallback contract */ event FallbackChanged(address indexed fallbackContract); /** * @notice Set a new fallback contract * @dev This function allows setting a new address as the fallback contract. The fallback contract will receive * all calls made to this contract that do not match any other function * @param fallbackContract The address of the fallback contract to be set */ function setFallbackHandler(address fallbackContract) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /** * @title AccountStorage * @notice A library that defines the storage layout for the SoulWallet account or contract. */ library AccountStorage { bytes32 internal constant _ACCOUNT_SLOT = keccak256("soulwallet.contracts.AccountStorage"); struct Layout { // base data mapping(bytes32 => bytes32) owners; address defaultFallbackContract; // validators mapping(address => address) validators; // hooks mapping(address => address) preIsValidSignatureHook; mapping(address => address) preUserOpValidationHook; // modules mapping(address => address) modules; mapping(address => mapping(bytes4 => bytes4)) moduleSelectors; } /** * @notice Returns the layout of the storage for the account or contract. * @return l The layout of the storage. */ function layout() internal pure returns (Layout storage l) { bytes32 slot = _ACCOUNT_SLOT; assembly ("memory-safe") { l.slot := slot } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; abstract contract FallbackManagerSnippet { /** * @dev Sets the address of the fallback handler contract * @param fallbackContract The address of the new fallback handler contract */ function _setFallbackHandler(address fallbackContract) internal virtual; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; struct Execution { // The target contract for account to execute. address target; // The value for the execution. uint256 value; // The call data for the execution. bytes data; } interface IStandardExecutor { /// @dev Standard execute method. /// @param target The target contract for account to execute. /// @param value The value for the execution. /// @param data The call data for the execution. function execute(address target, uint256 value, bytes calldata data) external payable; /// @dev Standard executeBatch method. /// @param executions The array of executions. function executeBatch(Execution[] calldata executions) external payable; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {IValidator} from "./IValidator.sol"; interface IValidatorManager { /** * @notice Emitted when a validator is installed * @param validator Validator */ event ValidatorInstalled(address validator); /** * @notice Emitted when a validator is uninstalled * @param validator Validator */ event ValidatorUninstalled(address validator); /** * @notice Emitted when a validator is uninstalled with error * @param validator Validator */ event ValidatorUninstalledwithError(address validator); function uninstallValidator(address validator) external; function listValidator() external view returns (address[] memory validators); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; import {PackedUserOperation} from "../interface/IAccount.sol"; import {IPluggable} from "./IPluggable.sol"; interface IValidator is IPluggable { /* NOTE: Any implementation must ensure that the `validatorSignature` exactly matches your expectations, otherwise, you will face security risks. For example: if you do not require any `validatorSignature`, make sure your implementation included the following code: `require(validatorSignature.length == 0)` */ /** * @dev EIP-1271 Should return whether the signature provided is valid for the provided data * @param sender Address of the message sender * @param hash Hash of the data to be signed * @param validatorSignature Signature byte array associated with _data */ function validateSignature(address sender, bytes32 hash, bytes memory validatorSignature) external view returns (bytes4 magicValue); /** * @dev EIP-4337 validate PackedUserOperation * NOTE: Do not rely on PackedUserOperation.signature, which may be empty in some versions of the implementation, see: /contracts/utils/CalldataPack.sol * @param userOp the operation that is about to be executed. * @param userOpHash hash of the user's request data. can be used as the basis for signature. * @param validatorSignature Signature * @return validationData packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, * otherwise, an address of an "authorizer" contract. * <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" * <6-byte> validAfter - first timestamp this operation is valid * If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure. * Note that the validation code cannot use block.timestamp (or block.number) directly. */ function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, bytes calldata validatorSignature) external returns (uint256 validationData); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /** * @title Address Linked List * @notice This library provides utility functions to manage a linked list of addresses */ library AddressLinkedList { error INVALID_ADDRESS(); error ADDRESS_ALREADY_EXISTS(); error ADDRESS_NOT_EXISTS(); address internal constant SENTINEL_ADDRESS = address(1); uint160 internal constant SENTINEL_UINT = 1; /** * @dev Modifier that checks if an address is valid. */ modifier onlyAddress(address addr) { if (uint160(addr) <= SENTINEL_UINT) { revert INVALID_ADDRESS(); } _; } /** * @notice Adds an address to the linked list. * @param self The linked list mapping. * @param addr The address to be added. */ function add(mapping(address => address) storage self, address addr) internal onlyAddress(addr) { if (self[addr] != address(0)) { revert ADDRESS_ALREADY_EXISTS(); } address _prev = self[SENTINEL_ADDRESS]; if (_prev == address(0)) { self[SENTINEL_ADDRESS] = addr; self[addr] = SENTINEL_ADDRESS; } else { self[SENTINEL_ADDRESS] = addr; self[addr] = _prev; } } /** * @notice Removes an address from the linked list. * @param self The linked list mapping. * @param addr The address to be removed. */ function remove(mapping(address => address) storage self, address addr) internal { if (!tryRemove(self, addr)) { revert ADDRESS_NOT_EXISTS(); } } /** * @notice Tries to remove an address from the linked list. * @param self The linked list mapping. * @param addr The address to be removed. * @return Returns true if removal is successful, false otherwise. */ function tryRemove(mapping(address => address) storage self, address addr) internal returns (bool) { if (isExist(self, addr)) { address cursor = SENTINEL_ADDRESS; while (true) { address _addr = self[cursor]; if (_addr == addr) { address next = self[_addr]; self[cursor] = next; self[_addr] = address(0); return true; } cursor = _addr; } } return false; } /** * @notice Clears all addresses from the linked list. * @param self The linked list mapping. */ function clear(mapping(address => address) storage self) internal { address addr = self[SENTINEL_ADDRESS]; self[SENTINEL_ADDRESS] = address(0); while (uint160(addr) > SENTINEL_UINT) { address _addr = self[addr]; self[addr] = address(0); addr = _addr; } } /** * @notice Checks if an address exists in the linked list. * @param self The linked list mapping. * @param addr The address to check. * @return Returns true if the address exists, false otherwise. */ function isExist(mapping(address => address) storage self, address addr) internal view onlyAddress(addr) returns (bool) { return self[addr] != address(0); } /** * @notice Returns the size of the linked list. * @param self The linked list mapping. * @return Returns the size of the linked list. */ function size(mapping(address => address) storage self) internal view returns (uint256) { uint256 result = 0; address addr = self[SENTINEL_ADDRESS]; while (uint160(addr) > SENTINEL_UINT) { addr = self[addr]; unchecked { result++; } } return result; } /** * @notice Checks if the linked list is empty. * @param self The linked list mapping. * @return Returns true if the linked list is empty, false otherwise. */ function isEmpty(mapping(address => address) storage self) internal view returns (bool) { return self[SENTINEL_ADDRESS] == address(0); } /** * @notice Returns a list of addresses from the linked list. * @param self The linked list mapping. * @param from The starting address. * @param limit The number of addresses to return. * @return Returns an array of addresses. */ function list(mapping(address => address) storage self, address from, uint256 limit) internal view returns (address[] memory) { address[] memory result = new address[](limit); uint256 i = 0; address addr = self[from]; while (uint160(addr) > SENTINEL_UINT && i < limit) { result[i] = addr; addr = self[addr]; unchecked { i++; } } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; //return value in case of signature failure, with no time-range. // equivalent to _packValidationData(true,0,0); uint256 constant SIG_VALIDATION_FAILED = 1; uint256 constant SIG_VALIDATION_SUCCESS = 0;
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; import {PackedUserOperation} from "../interface/IAccount.sol"; abstract contract ValidatorManagerSnippet { /** * @dev checks whether a address is a installed validator */ function _isInstalledValidator(address validator) internal view virtual returns (bool); /** * @dev checks whether a address is a valid validator * note: If you need to extend the interface, override this function * @param validator validator address */ function _isSupportsValidatorInterface(address validator) internal view virtual returns (bool); /** * @dev install a validator */ function _installValidator(address validator, bytes memory initData) internal virtual; /** * @dev uninstall a validator */ function _uninstallValidator(address validator) internal virtual; /** * @dev EIP-1271 * @param hash hash of the data to be signed * @param validator validator address * @param validatorSignature Signature byte array associated with _data * @return magicValue Magic value 0x1626ba7e if the validator is registered and signature is valid */ function _isValidSignature(bytes32 hash, address validator, bytes calldata validatorSignature) internal view virtual returns (bytes4 magicValue); /** * @dev validate UserOperation * @param userOp UserOperation * @param userOpHash UserOperation hash * @param validator validator address * @param validatorSignature validator signature * @return validationData refer to https://github.com/eth-infinitism/account-abstraction/blob/v0.6.0/contracts/interfaces/IAccount.sol#L24-L30 */ function _validateUserOp( PackedUserOperation calldata userOp, bytes32 userOpHash, address validator, bytes calldata validatorSignature ) internal virtual returns (uint256 validationData); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @title Pluggable Interface * @dev This interface provides functionalities for initializing and deinitializing wallet-related plugins or modules */ interface IPluggable is IERC165 { /** * @notice Initializes a specific module or plugin for the wallet with the provided data * @param data Initialization data required for the module or plugin */ function Init(bytes calldata data) external; /* NOTE: All implemention must ensure that the DeInit() function can be covered by 100,000 gas in all scenarios. */ /** * @notice Deinitializes a specific module or plugin from the wallet */ function DeInit() external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; import {PackedUserOperation} from "@account-abstraction/contracts/interfaces/PackedUserOperation.sol"; import {IValidator} from "../interface/IValidator.sol"; library CallDataPack { /** * @dev Executing abi.encodeWithSelector with a custom function can save at least 1623 gas (when the signature length is 89), and the savings are even greater in cases where userOp contains a longer signature or hookData. * * Benchmark: `forge test -vv --match-contract "CalldataPackTest" | grep 'gasDiff'` * Result: * gasDiff_EOASignature: 1623 * gasDiff_es256: 2239 * gasDiff_es256 with 1k hookdata 7284 * gasDiff_es256 with 2k hookdata 12371 * * Whether to remove userOp.signature from calldata depends on the position of userOp.signature * If userOp.signature is not the last field, it will be included in the encoded result * If userOp.signature is the last field, it will not be included in the encoded result */ function encodeWithoutUserOpSignature_validateUserOp_UserOperation_bytes32_bytes( PackedUserOperation calldata userOp, bytes32 userOpHash, bytes calldata validatorSignature ) internal pure returns (bytes memory callData) { /* Equivalent code: UserOperation memory _userOp = userOp; _userOp.signature = ""; bytes memory callData = abi.encodeWithSelector(IValidator.validateUserOp.selector, _userOp, userOpHash, validatorSignature); */ /* struct UserOperation { address sender; uint256 nonce; bytes initCode; bytes callData; bytes32 accountGasLimits; uint256 preVerificationGas; bytes32 maxPriorityFeePerGas & maxFeePerGas; bytes paymasterAndData; bytes signature; } In calldata, the data structure of the UserOperation is always: refer to:https://docs.soliditylang.org/en/develop/abi-spec.html offset: 0x00 000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # sender offset: 0x20 000000000000000000000000000000000000000000000000000b0b0b0b0b0b0b # nonce offset: 0x40 0000000000000000000000000000000000000000000000000000000000000160 # initCode offset offset: 0x60 00000000000000000000000000000000000000000000000000000000000001a0 # callData offset offset: 0x80 000000000000000000000000000000000000000000000000000e0e0e0e0e0e0e # accountGasLimits offset: 0xa0 0000000000000000000000000000000000000000000000000010101010101010 # preVerificationGas offset: 0xc0 0000000000000000000000000000000000000000000000000011111111111111 # maxPriorityFeePerGas & maxFeePerGas offset: 0xe0 00000000000000000000000000000000000000000000000000000000000001e0 # paymasterAndData offset offset:0x100 0000000000000000000000000000000000000000000000000000000000000220 # signature offset xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ──┐ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx │ dynamic data xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx │ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ──┘ In the known SDK implementation, the last field `signature` of the struct is packed at the end. However, manual encoding allows the position of signature to be moved earlier (still valid data). In our process, we detect the position of signature. If it's not the last field, we fallback to encoding with `abi.encodeWithSelector()`. If signature is detected as the last field, we can construct calldata with low cost using calldatacopy. */ // Check if userOp.signature is the last field, if not, fallback to `abi.encodeWithSelector()` { uint256 lastfield = 0; // 0: last field is signature, 1: last field is not signature assembly ("memory-safe") { let userOpOffset := userOp let _initCodeOffset := calldataload(add(userOpOffset, 0x40)) let _signatureOffset := calldataload(add(userOpOffset, 0x100)) // if(_initCodeOffset >= _signatureOffset) { lastfield = 1 } if iszero(lt(_initCodeOffset, _signatureOffset)) { lastfield := 1 } let _callDataOffset := calldataload(add(userOpOffset, 0x60)) // if(_callDataOffset >= _signatureOffset) { lastfield = 1 } if iszero(lt(_callDataOffset, _signatureOffset)) { lastfield := 1 } let _paymasterAndDataOffset := calldataload(add(userOpOffset, 0xe0)) // if(_paymasterAndDataOffset >= _signatureOffset) { lastfield = 1 } if iszero(lt(_paymasterAndDataOffset, _signatureOffset)) { lastfield := 1 } } if (lastfield == 1) { return abi.encodeWithSelector(IValidator.validateUserOp.selector, userOp, userOpHash, validatorSignature); } } /** * The validatorSignature comes from the slice of the Signature, * so don't try to get the length of the validatorSignature via calldataload(sub(validatorSignature.offset,32)) */ uint256 validatorSignatureLength = validatorSignature.length; bytes4 selector = IValidator.validateUserOp.selector; assembly ("memory-safe") { /* The structure of calldata is: refer to:https://docs.soliditylang.org/en/develop/abi-spec.html 5d719936 # selector 0000000000000000000000000000000000000000000000000000000000000060 # offset of userOp 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a # userOpHash 00000000000000000000000000000000000000000000000000000000000002a0 # offset of validatorSignature 000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ──┐ 000000000000000000000000000000000000000000000000000b0b0b0b0b0b0b │ 0000000000000000000000000000000000000000000000000000000000000160 │ 000000000000000000000000000000000000000000000000000f0f0f0f0f0f0f │ 0000000000000000000000000000000000000000000000000010101010101010 │ 0000000000000000000000000000000000000000000000000011111111111111 ├── userOp (signature length is 0) 0000000000000000000000000000000000000000000000000012121212121212 │ 00000000000000000000000000000000000000000000000000000000000001e0 │ ............... │ ............... │ ............... ──┴── (validatorSignature length is 0) 0000000000000000000000000000000000000000000000000000000000000020 # validatorSignature length xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # validatorSignature */ // Offset of userOp in calldata let userOpOffset := userOp let _signatureOffset := calldataload(add(userOpOffset, 0x100)) /* Length of userOp, excluding signature, but includes an additional 32bytes for recording the length of signature = 0 The length should be a multiple of 32, so no need for alignment like ValidatorSignatureLength */ let paddedUserOpLength := add(_signatureOffset, 32) // Round up to the nearest multiple of 32, bytes<M>: enc(X) is the sequence of bytes in X padded with trailing zero-bytes to a length of 32 bytes. let paddedValidatorSignatureLength := mul(div(add(validatorSignatureLength, 31), 32), 32) // Total length of call data: 4(selecter) + 32(offset of userOp) + 32(userOpHash) + 32(offset of validatorSignature) + paddedUserOpLength + 32(validatorSignature length)+ paddedValidatorSignatureLength let callDataLength := add(add(132, paddedUserOpLength), paddedValidatorSignatureLength) // Allocate memory: callDataLength - 4 bytes header + 32 (padding 4 bytes header to 32 bytes) + 32 (record data length) let ptr := mload(0x40) mstore(0x40, add(add(32, ptr), add(32, add(callDataLength, 28)))) // 4 bytes header and callData length, shift callData length 4bytes to align with selector in the same storage slot mstore(add(0x20, ptr), or(shl(32, callDataLength), shr(224, selector))) // Shift 4-byte header right by 8*(32-4) // offset of userOp mstore(add(0x40, ptr), 0x60) // userOp Hash mstore(add(0x60, ptr), userOpHash) // offset of validatorSignature, paddedUserOpLength+ 3*0x20 mstore(add(0x80, ptr), add(paddedUserOpLength, 0x60)) // userOp calldatacopy(add(0xa0, ptr), userOpOffset, paddedUserOpLength) let validatorSignatureOffset := add(add(0xa0, ptr), paddedUserOpLength) /* Set the signature length to 0, as an extra 32bytes data (original length of signature) was copied in the previous step of calldatacopy */ mstore(sub(validatorSignatureOffset, 32), 0) // validatorSignature length mstore(validatorSignatureOffset, validatorSignatureLength) // validatorSignature calldatacopy(add(0x20, validatorSignatureOffset), validatorSignature.offset, paddedValidatorSignatureLength) // Non-32 alignment, 0x20-0x04, to follow bytes4-selecter immediately after callData length callData := add(ptr, 28) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {Authority} from "./Authority.sol"; import {IOwnerManager} from "../interface/IOwnerManager.sol"; import {AccountStorage} from "../utils/AccountStorage.sol"; import {Bytes32LinkedList} from "../utils/Bytes32LinkedList.sol"; import {OwnerManagerSnippet} from "../snippets/OwnerManager.sol"; abstract contract OwnerManager is IOwnerManager, Authority, OwnerManagerSnippet { using Bytes32LinkedList for mapping(bytes32 => bytes32); /** * @notice Helper function to get the owner mapping from account storage * @return owners Mapping of current owners */ function _ownerMapping() internal view override returns (mapping(bytes32 => bytes32) storage owners) { owners = AccountStorage.layout().owners; } /** * @notice Checks if the provided owner is a current owner * @param owner Address in bytes32 format to check * @return true if provided owner is a current owner, false otherwise */ function _isOwner(bytes32 owner) internal view virtual override returns (bool) { return _ownerMapping().isExist(owner); } /** * @notice External function to check if the provided owner is a current owner * @param owner Address in bytes32 format to check * @return true if provided owner is a current owner, false otherwise */ function isOwner(bytes32 owner) external view virtual override returns (bool) { return _isOwner(owner); } /** * @notice Internal function to add an owner * @param owner Address in bytes32 format to add */ function _addOwner(bytes32 owner) internal virtual override { _ownerMapping().add(owner); emit OwnerAdded(owner); } /** * @notice add an owner * @param owner Address in bytes32 format to add */ function addOwner(bytes32 owner) external virtual override { ownerManagementAccess(); _addOwner(owner); } /** * @notice Internal function to remove an owner * @param owner Address in bytes32 format to remove */ function _removeOwner(bytes32 owner) internal virtual override { _ownerMapping().remove(owner); emit OwnerRemoved(owner); } /** * @notice remove an owner * @param owner Address in bytes32 format to remove */ function removeOwner(bytes32 owner) external virtual override { ownerManagementAccess(); _removeOwner(owner); } function _resetOwner(bytes32 newOwner) internal virtual override { _clearOwner(); _ownerMapping().add(newOwner); } function _clearOwner() internal virtual override { _ownerMapping().clear(); emit OwnerCleared(); } function resetOwner(bytes32 newOwner) external virtual override { ownerManagementAccess(); _resetOwner(newOwner); } function listOwner() external view virtual override returns (bytes32[] memory owners) { mapping(bytes32 => bytes32) storage _owners = _ownerMapping(); owners = _owners.list(Bytes32LinkedList.SENTINEL_BYTES32, _owners.size()); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {IOwnerManager} from "@soulwallet-core/contracts/interface/IOwnerManager.sol"; interface IElytroOwnerManager is IOwnerManager { function addOwners(bytes32[] calldata owners) external; function resetOwners(bytes32[] calldata newOwners) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; import {IModule} from "../interface/IModule.sol"; import {IPluggable} from "../interface/IPluggable.sol"; import {IModuleManager} from "../interface/IModuleManager.sol"; import {AccountStorage} from "../utils/AccountStorage.sol"; import {Authority} from "./Authority.sol"; import {AddressLinkedList} from "../utils/AddressLinkedList.sol"; import {SelectorLinkedList} from "../utils/SelectorLinkedList.sol"; import {ModuleManagerSnippet} from "../snippets/ModuleManager.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; abstract contract ModuleManager is IModuleManager, Authority, ModuleManagerSnippet { using AddressLinkedList for mapping(address => address); using SelectorLinkedList for mapping(bytes4 => bytes4); error MODULE_EXECUTE_FROM_MODULE_RECURSIVE(); error INVALID_MODULE(); error MODULE_NOT_EXISTS(); error MOUDLE_ALREADY_EXISTS(); error CALLER_MUST_BE_AUTHORIZED_MODULE(); bytes4 private constant INTERFACE_ID_MODULE = type(IModule).interfaceId; function _moduleMapping() internal view returns (mapping(address => address) storage modules) { modules = AccountStorage.layout().modules; } /** * @dev checks whether the caller is a authorized module * caller: msg.sender * method: msg.sig * @return bool */ function _isAuthorizedModule() internal view override returns (bool) { return AccountStorage.layout().moduleSelectors[msg.sender].isExist(msg.sig); } /** * @dev checks whether a address is a authorized module */ function _isInstalledModule(address module) internal view virtual override returns (bool) { return _moduleMapping().isExist(module); } /** * @dev checks whether a address is a installed module */ function isInstalledModule(address module) external view override returns (bool) { return _isInstalledModule(module); } /** * @dev checks whether a address is a module * note: If you need to extend the interface, override this function * @param moduleAddress module address */ function _isSupportsModuleInterface(address moduleAddress) internal view virtual override returns (bool supported) { bytes memory callData = abi.encodeWithSelector(IERC165.supportsInterface.selector, INTERFACE_ID_MODULE); assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := staticcall(gas(), moduleAddress, add(callData, 0x20), mload(callData), 0x00, 0x20) if and(result, eq(returndatasize(), 32)) { supported := mload(0x00) } } } /** * @dev install a module * * During the installation process of a module (even if the installation ultimately fails), * the module retains all of its permissions. This allows the module to execute highly * customized operations during the installation process, but it also comes with risks. * To mitigate these risks, it is recommended that users only install modules that have * been audited and are trusted. * * @param moduleAddress module address * @param initData module init data * @param selectors function selectors that the module is allowed to call */ function _installModule(address moduleAddress, bytes memory initData, bytes4[] memory selectors) internal virtual override { if (_isInstalledModule(moduleAddress)) { revert MOUDLE_ALREADY_EXISTS(); } if (_isSupportsModuleInterface(moduleAddress) == false) { revert INVALID_MODULE(); } mapping(address => address) storage modules = _moduleMapping(); modules.add(moduleAddress); mapping(bytes4 => bytes4) storage moduleSelectors = AccountStorage.layout().moduleSelectors[moduleAddress]; for (uint256 i = 0; i < selectors.length; i++) { moduleSelectors.add(selectors[i]); } bytes memory callData = abi.encodeWithSelector(IPluggable.Init.selector, initData); bytes4 invalidModuleSelector = INVALID_MODULE.selector; assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := call(gas(), moduleAddress, 0, add(callData, 0x20), mload(callData), 0x00, 0x00) if iszero(result) { mstore(0x00, invalidModuleSelector) revert(0x00, 4) } } emit ModuleInstalled(moduleAddress); } /** * @dev uninstall a module * @param moduleAddress module address */ function _uninstallModule(address moduleAddress) internal virtual override { mapping(address => address) storage modules = _moduleMapping(); if (!modules.tryRemove(moduleAddress)) { revert MODULE_NOT_EXISTS(); } AccountStorage.layout().moduleSelectors[moduleAddress].clear(); (bool success,) = moduleAddress.call(abi.encodeWithSelector(IPluggable.DeInit.selector)); if (success) { emit ModuleUninstalled(moduleAddress); } else { emit ModuleUninstalledwithError(moduleAddress); } } /** * @dev uninstall a module * @param moduleAddress module address */ function uninstallModule(address moduleAddress) external virtual override { pluginManagementAccess(); _uninstallModule(moduleAddress); } /** * @dev Provides a list of all added modules and their respective authorized function selectors * @return modules An array of the addresses of all added modules * @return selectors A 2D array where each inner array represents the function selectors * that the corresponding module in the 'modules' array is allowed to call */ function listModule() external view virtual override returns (address[] memory modules, bytes4[][] memory selectors) { mapping(address => address) storage _modules = _moduleMapping(); uint256 moduleSize = _moduleMapping().size(); modules = new address[](moduleSize); mapping(address => mapping(bytes4 => bytes4)) storage moduleSelectors = AccountStorage.layout().moduleSelectors; selectors = new bytes4[][](moduleSize); uint256 i = 0; address addr = _modules[AddressLinkedList.SENTINEL_ADDRESS]; while (uint160(addr) > AddressLinkedList.SENTINEL_UINT) { { modules[i] = addr; mapping(bytes4 => bytes4) storage moduleSelector = moduleSelectors[addr]; { uint256 selectorSize = moduleSelector.size(); bytes4[] memory _selectors = new bytes4[](selectorSize); uint256 j = 0; bytes4 selector = moduleSelector[SelectorLinkedList.SENTINEL_SELECTOR]; while (uint32(selector) > SelectorLinkedList.SENTINEL_UINT) { _selectors[j] = selector; selector = moduleSelector[selector]; unchecked { j++; } } selectors[i] = _selectors; } } addr = _modules[addr]; unchecked { i++; } } } /** * @notice Allows a module to execute a function within the system. This ensures that the * module can only call functions it is permitted to. * @param dest The address of the destination contract where the function will be executed * @param value The amount of ether (in wei) to be sent with the function call * @param func The function data to be executed */ function executeFromModule(address dest, uint256 value, bytes memory func) external virtual override { if (_isAuthorizedModule() == false) { revert CALLER_MUST_BE_AUTHORIZED_MODULE(); } if (dest == address(this)) revert MODULE_EXECUTE_FROM_MODULE_RECURSIVE(); assembly ("memory-safe") { // memorySafe: Memory allocated by yourself using a mechanism like the allocate function described above. function allocate(length) -> pos { pos := mload(0x40) mstore(0x40, add(pos, length)) } let result := call(gas(), dest, value, add(func, 0x20), mload(func), 0, 0) let returndataPtr := allocate(returndatasize()) returndatacopy(returndataPtr, 0, returndatasize()) if iszero(result) { revert(returndataPtr, returndatasize()) } return(returndataPtr, returndatasize()) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {IModuleManager} from "@soulwallet-core/contracts/interface/IModuleManager.sol"; interface IElytroModuleManager is IModuleManager { function installModule(bytes calldata moduleAndData) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.20; import {IModule} from "@soulwallet-core/contracts/interface/IModule.sol"; interface IElytroModule is IModule { function requiredFunctions() external pure returns (bytes4[] memory); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; import {Authority} from "./Authority.sol"; import {IHookManager} from "../interface/IHookManager.sol"; import {IHook} from "../interface/IHook.sol"; import {IPluggable} from "../interface/IPluggable.sol"; import {IAccount, PackedUserOperation} from "../interface/IAccount.sol"; import {AccountStorage} from "../utils/AccountStorage.sol"; import {AddressLinkedList} from "../utils/AddressLinkedList.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {SIG_VALIDATION_FAILED} from "../utils/Constants.sol"; import {HookManagerSnippet} from "../snippets/HookManager.sol"; abstract contract HookManager is Authority, IHookManager, HookManagerSnippet { using AddressLinkedList for mapping(address => address); error INVALID_HOOK(); error INVALID_HOOK_TYPE(); error HOOK_NOT_EXISTS(); error HOOK_ALREADY_EXISTS(); error INVALID_HOOK_SIGNATURE(); bytes4 private constant INTERFACE_ID_HOOK = type(IHook).interfaceId; /* Capability flags for the hook: 0x01: preIsValidSignatureHook: execute before isValidSignature 0x02: preUserOpValidationHook: execute before validateUserOp */ uint8 internal constant PRE_IS_VALID_SIGNATURE_HOOK = 1 << 0; uint8 internal constant PRE_USER_OP_VALIDATION_HOOK = 1 << 1; /** * @dev Check if the hook is installed * @param hook The address of the hook */ function isInstalledHook(address hook) external view override returns (bool) { return _isInstalledHook(hook); } /** * @dev checks whether a address is a installed hook */ function _isInstalledHook(address hook) internal view virtual override returns (bool) { return AccountStorage.layout().preUserOpValidationHook.isExist(hook) || AccountStorage.layout().preIsValidSignatureHook.isExist(hook); } /** * @dev checks whether a address is a valid hook * note: If you need to extend the interface, override this function * @param hookAddress hook address */ function _isSupportsHookInterface(address hookAddress) internal view virtual override returns (bool supported) { bytes memory callData = abi.encodeWithSelector(IERC165.supportsInterface.selector, INTERFACE_ID_HOOK); assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := staticcall(gas(), hookAddress, add(callData, 0x20), mload(callData), 0x00, 0x20) if and(result, eq(returndatasize(), 32)) { supported := mload(0x00) } } } /** * @dev Install a hook * * During the installation process of a hook (even if the installation ultimately fails), * the hook retains all of its permissions. This allows the hook to execute highly * customized operations during the installation process, but it also comes with risks. * To mitigate these risks, it is recommended that users only install hooks that have * been audited and are trusted. * * @param hookAddress The address of the hook * @param initData The init data of the hook * @param capabilityFlags Capability flags for the hook */ function _installHook(address hookAddress, bytes memory initData, uint8 capabilityFlags) internal virtual override { if (_isInstalledHook(hookAddress)) { revert HOOK_ALREADY_EXISTS(); } if (_isSupportsHookInterface(hookAddress) == false) { revert INVALID_HOOK(); } if (capabilityFlags & (PRE_USER_OP_VALIDATION_HOOK | PRE_IS_VALID_SIGNATURE_HOOK) == 0) { revert INVALID_HOOK_TYPE(); } if (capabilityFlags & PRE_IS_VALID_SIGNATURE_HOOK == PRE_IS_VALID_SIGNATURE_HOOK) { AccountStorage.layout().preIsValidSignatureHook.add(hookAddress); } if (capabilityFlags & PRE_USER_OP_VALIDATION_HOOK == PRE_USER_OP_VALIDATION_HOOK) { AccountStorage.layout().preUserOpValidationHook.add(hookAddress); } bytes4 invalidHookSelector = INVALID_HOOK.selector; bytes memory callData = abi.encodeWithSelector(IPluggable.Init.selector, initData); assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := call(gas(), hookAddress, 0, add(callData, 0x20), mload(callData), 0x00, 0x00) if iszero(result) { mstore(0x00, invalidHookSelector) revert(0x00, 4) } } emit HookInstalled(hookAddress); } /** * @dev Uninstall a hook * 1. revert if the hook is not installed * 2. call hook.deInit() with 1M gas, emit HOOK_UNINSTALL_WITHERROR if the call failed * @param hookAddress The address of the hook */ function _uninstallHook(address hookAddress) internal virtual override { bool removed1 = AccountStorage.layout().preIsValidSignatureHook.tryRemove(hookAddress); bool removed2 = AccountStorage.layout().preUserOpValidationHook.tryRemove(hookAddress); if (removed1 == false && removed2 == false) { revert HOOK_NOT_EXISTS(); } (bool success,) = hookAddress.call(abi.encodeWithSelector(IPluggable.DeInit.selector)); if (success) { emit HookUninstalled(hookAddress); } else { emit HookUninstalledwithError(hookAddress); } } /** * @dev Uninstall a hook * @param hookAddress The address of the hook */ function uninstallHook(address hookAddress) external virtual override { pluginManagementAccess(); _uninstallHook(hookAddress); } /** * @dev List all installed hooks */ function listHook() external view virtual override returns (address[] memory preIsValidSignatureHooks, address[] memory preUserOpValidationHooks) { mapping(address => address) storage preIsValidSignatureHook = AccountStorage.layout().preIsValidSignatureHook; preIsValidSignatureHooks = preIsValidSignatureHook.list(AddressLinkedList.SENTINEL_ADDRESS, preIsValidSignatureHook.size()); mapping(address => address) storage preUserOpValidationHook = AccountStorage.layout().preUserOpValidationHook; preUserOpValidationHooks = preUserOpValidationHook.list(AddressLinkedList.SENTINEL_ADDRESS, preUserOpValidationHook.size()); } /** * @dev Get the next hook signature * @param hookSignatures The hook signatures * @param cursor The cursor of the hook signatures */ function _nextHookSignature(bytes calldata hookSignatures, uint256 cursor) private pure returns (address _hookAddr, uint256 _cursorFrom, uint256 _cursorEnd) { /* +--------------------------------------------------------------------------------+ | multi-hookSignature | +--------------------------------------------------------------------------------+ | hookSignature | hookSignature | ... | hookSignature | +-----------------------+--------------------------------------------------------+ | dynamic data | dynamic data | ... | dynamic data | +--------------------------------------------------------------------------------+ +----------------------------------------------------------------------+ | hookSignature | +----------------------------------------------------------------------+ | Hook address | hookSignature length | hookSignature | +----------------------+-----------------------------------------------+ | 20bytes | 4bytes(uint32) | bytes | +----------------------------------------------------------------------+ */ uint256 dataLen = hookSignatures.length; if (dataLen > cursor) { assembly ("memory-safe") { let ptr := add(hookSignatures.offset, cursor) _hookAddr := shr(0x60, calldataload(ptr)) if iszero(_hookAddr) { revert(0, 0) } _cursorFrom := add(cursor, 24) //20+4 let guardSigLen := shr(0xe0, calldataload(add(ptr, 20))) if iszero(guardSigLen) { revert(0, 0) } _cursorEnd := add(_cursorFrom, guardSigLen) } } } /** * @dev Call preIsValidSignatureHook for all installed hooks * @param hash The hash of the data to be signed * @param hookSignatures The hook signatures */ function _preIsValidSignatureHook(bytes32 hash, bytes calldata hookSignatures) internal view virtual { address _hookAddr; uint256 _cursorFrom; uint256 _cursorEnd; (_hookAddr, _cursorFrom, _cursorEnd) = _nextHookSignature(hookSignatures, _cursorEnd); mapping(address => address) storage preIsValidSignatureHook = AccountStorage.layout().preIsValidSignatureHook; address hookAddress = preIsValidSignatureHook[AddressLinkedList.SENTINEL_ADDRESS]; while (uint160(hookAddress) > AddressLinkedList.SENTINEL_UINT) { bytes calldata currentHookSignature; if (hookAddress == _hookAddr) { currentHookSignature = hookSignatures[_cursorFrom:_cursorEnd]; // next _hookAddr = address(0); if (_cursorEnd > 0) { (_hookAddr, _cursorFrom, _cursorEnd) = _nextHookSignature(hookSignatures, _cursorEnd); } } else { currentHookSignature = hookSignatures[0:0]; } bytes memory callData = abi.encodeWithSelector(IHook.preIsValidSignatureHook.selector, hash, currentHookSignature); assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := staticcall(gas(), hookAddress, add(callData, 0x20), mload(callData), 0x00, 0x00) if iszero(result) { /* Warning!!! This function uses `return` to terminate the execution of the entire contract. If any `Hook` fails, this function will stop the contract's execution and return `bytes4(0)`, skipping all the subsequent unexecuted code. */ mstore(0x00, 0x00000000) return(0x00, 0x20) } } hookAddress = preIsValidSignatureHook[hookAddress]; } if (_hookAddr != address(0)) { revert INVALID_HOOK_SIGNATURE(); } } /** * @dev Call preUserOpValidationHook for all installed hooks * * Warning!!! * This function uses `return` to terminate the execution of the entire contract. * If any `Hook` fails, this function will stop the contract's execution and * return `SIG_VALIDATION_FAILED`, skipping all the subsequent unexecuted code. * * * * @param userOp The UserOperation * @param userOpHash The hash of the UserOperation * @param missingAccountFunds The missing account funds * @param hookSignatures The hook signatures */ function _preUserOpValidationHook( PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds, bytes calldata hookSignatures ) internal virtual { address _hookAddr; uint256 _cursorFrom; uint256 _cursorEnd; (_hookAddr, _cursorFrom, _cursorEnd) = _nextHookSignature(hookSignatures, _cursorEnd); mapping(address => address) storage preUserOpValidationHook = AccountStorage.layout().preUserOpValidationHook; address hookAddress = preUserOpValidationHook[AddressLinkedList.SENTINEL_ADDRESS]; while (uint160(hookAddress) > AddressLinkedList.SENTINEL_UINT) { bytes calldata currentHookSignature; if (hookAddress == _hookAddr) { currentHookSignature = hookSignatures[_cursorFrom:_cursorEnd]; // next _hookAddr = address(0); if (_cursorEnd > 0) { (_hookAddr, _cursorFrom, _cursorEnd) = _nextHookSignature(hookSignatures, _cursorEnd); } } else { currentHookSignature = hookSignatures[0:0]; } bytes memory callData = abi.encodeWithSelector( IHook.preUserOpValidationHook.selector, userOp, userOpHash, missingAccountFunds, currentHookSignature ); assembly ("memory-safe") { // memorySafe: The scratch space between memory offset 0 and 64. let result := call(gas(), hookAddress, 0, add(callData, 0x20), mload(callData), 0x00, 0x00) if iszero(result) { /* Warning!!! This function uses `return` to terminate the execution of the entire contract. If any `Hook` fails, this function will stop the contract's execution and return `SIG_VALIDATION_FAILED`, skipping all the subsequent unexecuted code. */ mstore(0x00, SIG_VALIDATION_FAILED) return(0x00, 0x20) } } hookAddress = preUserOpValidationHook[hookAddress]; } if (_hookAddr != address(0)) { revert INVALID_HOOK_SIGNATURE(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {IHookManager} from "@soulwallet-core/contracts/interface/IHookManager.sol"; interface IElytroHookManager is IHookManager { function installHook(bytes calldata hookAndData, uint8 capabilityFlags) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.20; /** * @title Upgradable Interface * @dev This interface provides functionalities to upgrade the implementation of a contract * It emits an event when the implementation is changed, either to a new version or from an old version */ interface IUpgradable { event Upgraded(address indexed oldImplementation, address indexed newImplementation); /** * @dev Upgrade the current implementation to the provided new implementation address * @param newImplementation The address of the new contract implementation */ function upgradeTo(address newImplementation) external; /** * @dev Upgrade from the current implementation, given the old implementation address * @param oldImplementation The address of the old contract implementation that is being replaced */ function upgradeFrom(address oldImplementation) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; abstract contract AuthoritySnippet { /** * @dev checks whether the caller is a authorized module * caller: msg.sender * method: msg.sig * @return bool */ function _isAuthorizedModule() internal view virtual returns (bool); /** * @notice Ensures the calling contract is the entrypoint */ function _onlyEntryPoint() internal view virtual; /** * @notice Ensures the calling contract is an authorized module */ function _onlyModule() internal view virtual; /** * @notice Ensures the calling contract is either the Authority contract itself or an authorized module * @dev Uses the inherited `_isAuthorizedModule()` from ModuleAuth for module-based authentication */ function _onlySelfOrModule() internal view virtual; /** * @dev Check if access to the following functions: * 1. setFallbackHandler */ function fallbackManagementAccess() internal view virtual; /** * @dev Check if access to the following functions: * 1. installHook * 2. uninstallHook * 3. installModule * 4. uninstallModule */ function pluginManagementAccess() internal view virtual; /** * @dev Check if access to the following functions: * 1. addOwner * 2. removeOwner * 3. resetOwner */ function ownerManagementAccess() internal view virtual; /** * @dev Check if access to the following functions: * 1. execute * 2. executeBatch */ function executorAccess() internal view virtual; /** * @dev Check if access to the following functions: * 1. installValidator * 2. uninstallValidator */ function validatorManagementAccess() internal view virtual; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {IOwnable} from "./IOwnable.sol"; interface IOwnerManager is IOwnable { /** * @notice Emitted when an owner is added * @param owner owner */ event OwnerAdded(bytes32 indexed owner); /** * @notice Emitted when an owner is removed * @param owner owner */ event OwnerRemoved(bytes32 indexed owner); /** * @notice Emitted when all owners are removed */ event OwnerCleared(); /** * @notice Adds a new owner to the system * @param owner The bytes32 ID of the owner to be added */ function addOwner(bytes32 owner) external; /** * @notice Removes an existing owner from the system * @param owner The bytes32 ID of the owner to be removed */ function removeOwner(bytes32 owner) external; /** * @notice Resets the entire owner set, replacing it with a single new owner * @param newOwner The bytes32 ID of the new owner */ function resetOwner(bytes32 newOwner) external; /** * @notice Provides a list of all added owners * @return owners An array of bytes32 IDs representing the owners */ function listOwner() external view returns (bytes32[] memory owners); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; library Bytes32LinkedList { error INVALID_DATA(); error DATA_ALREADY_EXISTS(); error DATA_NOT_EXISTS(); bytes32 internal constant SENTINEL_BYTES32 = 0x0000000000000000000000000000000000000000000000000000000000000001; modifier onlyBytes32(bytes32 data) { if (data <= SENTINEL_BYTES32) { revert INVALID_DATA(); } _; } function add(mapping(bytes32 => bytes32) storage self, bytes32 data) internal onlyBytes32(data) { if (self[data] != bytes32(0)) { revert DATA_ALREADY_EXISTS(); } bytes32 _prev = self[SENTINEL_BYTES32]; if (_prev == bytes32(0)) { self[SENTINEL_BYTES32] = data; self[data] = SENTINEL_BYTES32; } else { self[SENTINEL_BYTES32] = data; self[data] = _prev; } } function remove(mapping(bytes32 => bytes32) storage self, bytes32 data) internal { if (!tryRemove(self, data)) { revert DATA_NOT_EXISTS(); } } function tryRemove(mapping(bytes32 => bytes32) storage self, bytes32 data) internal returns (bool) { if (isExist(self, data)) { bytes32 cursor = SENTINEL_BYTES32; while (true) { bytes32 _data = self[cursor]; if (_data == data) { bytes32 next = self[_data]; self[cursor] = next; self[_data] = bytes32(0); return true; } cursor = _data; } } return false; } function clear(mapping(bytes32 => bytes32) storage self) internal { bytes32 data = self[SENTINEL_BYTES32]; self[SENTINEL_BYTES32] = bytes32(0); while (data > SENTINEL_BYTES32) { bytes32 _data = self[data]; self[data] = bytes32(0); data = _data; } } function isExist(mapping(bytes32 => bytes32) storage self, bytes32 data) internal view onlyBytes32(data) returns (bool) { return self[data] != bytes32(0); } function size(mapping(bytes32 => bytes32) storage self) internal view returns (uint256) { uint256 result = 0; bytes32 data = self[SENTINEL_BYTES32]; while (data > SENTINEL_BYTES32) { data = self[data]; unchecked { result++; } } return result; } function isEmpty(mapping(bytes32 => bytes32) storage self) internal view returns (bool) { return self[SENTINEL_BYTES32] == bytes32(0); } function list(mapping(bytes32 => bytes32) storage self, bytes32 from, uint256 limit) internal view returns (bytes32[] memory) { bytes32[] memory result = new bytes32[](limit); uint256 i = 0; bytes32 data = self[from]; while (data > SENTINEL_BYTES32 && i < limit) { result[i] = data; data = self[data]; unchecked { i++; } } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; abstract contract OwnerManagerSnippet { /** * @dev Helper function to get the owner mapping from account storage * @return owners Mapping of current owners */ function _ownerMapping() internal view virtual returns (mapping(bytes32 => bytes32) storage owners); /** * @dev Checks if the provided owner is a current owner * @param owner Address in bytes32 format to check * @return true if provided owner is a current owner, false otherwise */ function _isOwner(bytes32 owner) internal view virtual returns (bool); /** * @dev Add an owner * @param owner Address in bytes32 format to add */ function _addOwner(bytes32 owner) internal virtual; /** * @dev Remove an owner * @param owner Address in bytes32 format to remove */ function _removeOwner(bytes32 owner) internal virtual; /** * @dev Reset owner */ function _resetOwner(bytes32 newOwner) internal virtual; /** * @dev Clear owner */ function _clearOwner() internal virtual; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import {IPluggable} from "./IPluggable.sol"; interface IModule is IPluggable { /* NOTE: All implemention must ensure that the DeInit() function can be covered by 100,000 gas in all scenarios. */ }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; interface IModuleManager { /** * @notice Emitted when a module is installed * @param module module */ event ModuleInstalled(address module); /** * @notice Emitted when a module is uninstalled * @param module module */ event ModuleUninstalled(address module); /** * @notice Emitted when a module is uninstalled with error * @param module module */ event ModuleUninstalledwithError(address module); function uninstallModule(address moduleAddress) external; function isInstalledModule(address module) external view returns (bool); /** * @notice Provides a list of all added modules and their respective authorized function selectors * @return modules An array of the addresses of all added modules * @return selectors A 2D array where each inner array represents the function selectors * that the corresponding module in the 'modules' array is allowed to call */ function listModule() external view returns (address[] memory modules, bytes4[][] memory selectors); /** * @notice Allows a module to execute a function within the system. This ensures that the * module can only call functions it is permitted to, based on its declared `requiredFunctions` * @param dest The address of the destination contract where the function will be executed * @param value The amount of ether (in wei) to be sent with the function call * @param func The function data to be executed */ function executeFromModule(address dest, uint256 value, bytes calldata func) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; library SelectorLinkedList { error INVALID_SELECTOR(); error SELECTOR_ALREADY_EXISTS(); error SELECTOR_NOT_EXISTS(); bytes4 internal constant SENTINEL_SELECTOR = 0x00000001; uint32 internal constant SENTINEL_UINT = 1; modifier onlySelector(bytes4 selector) { if (uint32(selector) <= SENTINEL_UINT) { revert INVALID_SELECTOR(); } _; } function add(mapping(bytes4 => bytes4) storage self, bytes4 selector) internal onlySelector(selector) { if (self[selector] != 0) { revert SELECTOR_ALREADY_EXISTS(); } bytes4 _prev = self[SENTINEL_SELECTOR]; if (_prev == 0) { self[SENTINEL_SELECTOR] = selector; self[selector] = SENTINEL_SELECTOR; } else { self[SENTINEL_SELECTOR] = selector; self[selector] = _prev; } } function add(mapping(bytes4 => bytes4) storage self, bytes4[] memory selectors) internal { for (uint256 i = 0; i < selectors.length;) { add(self, selectors[i]); unchecked { i++; } } } function remove(mapping(bytes4 => bytes4) storage self, bytes4 selector) internal { if (!isExist(self, selector)) { revert SELECTOR_NOT_EXISTS(); } bytes4 cursor = SENTINEL_SELECTOR; while (true) { bytes4 _selector = self[cursor]; if (_selector == selector) { bytes4 next = self[_selector]; if (next == SENTINEL_SELECTOR && cursor == SENTINEL_SELECTOR) { self[SENTINEL_SELECTOR] = 0; } else { self[cursor] = next; } self[_selector] = 0; return; } cursor = _selector; } } function clear(mapping(bytes4 => bytes4) storage self) internal { bytes4 selector = self[SENTINEL_SELECTOR]; self[SENTINEL_SELECTOR] = 0; while (uint32(selector) > SENTINEL_UINT) { bytes4 _selector = self[selector]; self[selector] = 0; selector = _selector; } } function isExist(mapping(bytes4 => bytes4) storage self, bytes4 selector) internal view onlySelector(selector) returns (bool) { return self[selector] != 0; } function size(mapping(bytes4 => bytes4) storage self) internal view returns (uint256) { uint256 result = 0; bytes4 selector = self[SENTINEL_SELECTOR]; while (uint32(selector) > SENTINEL_UINT) { selector = self[selector]; unchecked { result++; } } return result; } function isEmpty(mapping(bytes4 => bytes4) storage self) internal view returns (bool) { return self[SENTINEL_SELECTOR] == 0; } function list(mapping(bytes4 => bytes4) storage self, bytes4 from, uint256 limit) internal view returns (bytes4[] memory) { bytes4[] memory result = new bytes4[](limit); uint256 i = 0; bytes4 selector = self[from]; while (uint32(selector) > SENTINEL_UINT && i < limit) { result[i] = selector; selector = self[selector]; unchecked { i++; } } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; abstract contract ModuleManagerSnippet { /** * @dev checks whether a address is a authorized module */ function _isInstalledModule(address module) internal view virtual returns (bool); /** * @dev checks whether a address is a module * note: If you need to extend the interface, override this function * @param moduleAddress module address */ function _isSupportsModuleInterface(address moduleAddress) internal view virtual returns (bool); /** * @dev install a module * @param moduleAddress module address * @param initData module init data * @param selectors function selectors that the module is allowed to call */ function _installModule(address moduleAddress, bytes memory initData, bytes4[] memory selectors) internal virtual; /** * @dev uninstall a module * @param moduleAddress module address */ function _uninstallModule(address moduleAddress) internal virtual; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; interface IHookManager { /** * @notice Emitted when a hook is installed * @param hook hook */ event HookInstalled(address hook); /** * @notice Emitted when a hook is uninstalled * @param hook hook */ event HookUninstalled(address hook); /** * @notice Emitted when a hook is uninstalled with error * @param hook hook */ event HookUninstalledwithError(address hook); function uninstallHook(address hookAddress) external; function isInstalledHook(address hook) external view returns (bool); function listHook() external view returns (address[] memory preIsValidSignatureHooks, address[] memory preUserOpValidationHooks); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; import {PackedUserOperation} from "../interface/IAccount.sol"; import {IPluggable} from "./IPluggable.sol"; interface IHook is IPluggable { /* NOTE: Any implementation must ensure that the `hookSignature` exactly matches your expectations, otherwise, you will face security risks. For example: if you do not require any `hookSignature`, make sure your implementation included the following code: `require(hookSignature.length == 0)` NOTE: All implemention must ensure that the DeInit() function can be covered by 100,000 gas in all scenarios. */ /** * @dev Should return whether the signature provided is valid for the provided data * @param hash Hash of the data to be signed * @param hookSignature Signature byte array associated with _data */ function preIsValidSignatureHook(bytes32 hash, bytes calldata hookSignature) external view; /** * @dev Hook that is called before any userOp is executed. * NOTE: Do not rely on userOperation.signature, which may be empty in some versions of the implementation. see: /contracts/utils/CalldataPack.sol * must revert if the userOp is invalid. */ function preUserOpValidationHook( PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds, bytes calldata hookSignature ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; abstract contract HookManagerSnippet { /** * @dev checks whether a address is a valid hook * note: If you need to extend the interface, override this function * @param hookAddress hook address */ function _isSupportsHookInterface(address hookAddress) internal view virtual returns (bool); /** * @dev checks whether a address is a installed hook */ function _isInstalledHook(address hook) internal view virtual returns (bool); /** * @dev Install a hook * @param hookAddress The address of the hook * @param initData The init data of the hook * @param capabilityFlags Capability flags for the hook */ function _installHook(address hookAddress, bytes memory initData, uint8 capabilityFlags) internal virtual; /** * @dev Uninstall a hook * 1. revert if the hook is not installed * 2. call hook.deInit() with 1M gas, emit HOOK_UNINSTALL_WITHERROR if the call failed * @param hookAddress The address of the hook */ function _uninstallHook(address hookAddress) internal virtual; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; interface IOwnable { /** * @notice Checks if a given bytes32 ID corresponds to an owner within the system * @param owner The bytes32 ID to check * @return True if the ID corresponds to an owner, false otherwise */ function isOwner(bytes32 owner) external view returns (bool); }
{ "remappings": [ "@soulwallet-core/=lib/soulwallet-core/", "@source/=contracts/", "@arbitrum/nitro-contracts/=lib/nitro-contracts/", "@solady/=lib/solady/", "@solenv/=lib/solenv/src/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@account-abstraction/=lib/account-abstraction/", "@crypto-lib/=lib/crypto-lib/src/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "account-abstraction/=lib/account-abstraction/contracts/", "ds-test/=lib/soulwallet-core/lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/", "nitro-contracts/=lib/nitro-contracts/src/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solady/=lib/solady/src/", "solenv/=lib/solenv/", "solidity-stringutils/=lib/solenv/lib/solidity-stringutils/", "soulwallet-core/=lib/soulwallet-core/" ], "optimizer": { "enabled": true, "runs": 100000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": false }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": true, "libraries": {} }
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_entryPoint","type":"address"},{"internalType":"address","name":"defaultValidator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ADDRESS_ALREADY_EXISTS","type":"error"},{"inputs":[],"name":"CALLER_MUST_BE_AUTHORIZED_MODULE","type":"error"},{"inputs":[],"name":"CALLER_MUST_BE_ENTRY_POINT","type":"error"},{"inputs":[],"name":"CALLER_MUST_BE_MODULE","type":"error"},{"inputs":[],"name":"CALLER_MUST_BE_SELF_OR_MODULE","type":"error"},{"inputs":[],"name":"DATA_ALREADY_EXISTS","type":"error"},{"inputs":[],"name":"DATA_NOT_EXISTS","type":"error"},{"inputs":[],"name":"HOOK_ALREADY_EXISTS","type":"error"},{"inputs":[],"name":"HOOK_NOT_EXISTS","type":"error"},{"inputs":[],"name":"INVALID_ADDRESS","type":"error"},{"inputs":[],"name":"INVALID_DATA","type":"error"},{"inputs":[],"name":"INVALID_HOOK","type":"error"},{"inputs":[],"name":"INVALID_HOOK_SIGNATURE","type":"error"},{"inputs":[],"name":"INVALID_HOOK_TYPE","type":"error"},{"inputs":[],"name":"INVALID_LOGIC_ADDRESS","type":"error"},{"inputs":[],"name":"INVALID_MODULE","type":"error"},{"inputs":[],"name":"INVALID_SELECTOR","type":"error"},{"inputs":[],"name":"INVALID_VALIDATOR","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"MODULE_EXECUTE_FROM_MODULE_RECURSIVE","type":"error"},{"inputs":[],"name":"MODULE_NOT_EXISTS","type":"error"},{"inputs":[],"name":"MODULE_SELECTORS_EMPTY","type":"error"},{"inputs":[],"name":"MOUDLE_ALREADY_EXISTS","type":"error"},{"inputs":[],"name":"NOT_IMPLEMENTED","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"SAME_LOGIC_ADDRESS","type":"error"},{"inputs":[],"name":"SELECTOR_ALREADY_EXISTS","type":"error"},{"inputs":[],"name":"UPGRADE_FAILED","type":"error"},{"inputs":[],"name":"VALIDATOR_ALREADY_EXISTS","type":"error"},{"inputs":[],"name":"VALIDATOR_NOT_EXISTS","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fallbackContract","type":"address"}],"name":"FallbackChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hook","type":"address"}],"name":"HookInstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hook","type":"address"}],"name":"HookUninstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hook","type":"address"}],"name":"HookUninstalledwithError","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"ModuleInstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"ModuleUninstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"ModuleUninstalledwithError","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"owner","type":"bytes32"}],"name":"OwnerAdded","type":"event"},{"anonymous":false,"inputs":[],"name":"OwnerCleared","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"owner","type":"bytes32"}],"name":"OwnerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"validator","type":"address"}],"name":"ValidatorInstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"validator","type":"address"}],"name":"ValidatorUninstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"validator","type":"address"}],"name":"ValidatorUninstalledwithError","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"owner","type":"bytes32"}],"name":"addOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"owners","type":"bytes32[]"}],"name":"addOwners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Execution[]","name":"executions","type":"tuple[]"}],"name":"executeBatch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"dest","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"func","type":"bytes"}],"name":"executeFromModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"owners","type":"bytes32[]"},{"internalType":"address","name":"defalutCallbackHandler","type":"address"},{"internalType":"bytes[]","name":"modules","type":"bytes[]"},{"internalType":"bytes[]","name":"hooks","type":"bytes[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"hookAndData","type":"bytes"},{"internalType":"uint8","name":"capabilityFlags","type":"uint8"}],"name":"installHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"moduleAndData","type":"bytes"}],"name":"installModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"hook","type":"address"}],"name":"isInstalledHook","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"isInstalledModule","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"owner","type":"bytes32"}],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"magicValue","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"listHook","outputs":[{"internalType":"address[]","name":"preIsValidSignatureHooks","type":"address[]"},{"internalType":"address[]","name":"preUserOpValidationHooks","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"listModule","outputs":[{"internalType":"address[]","name":"modules","type":"address[]"},{"internalType":"bytes4[][]","name":"selectors","type":"bytes4[][]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"listOwner","outputs":[{"internalType":"bytes32[]","name":"owners","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"listValidator","outputs":[{"internalType":"address[]","name":"validators","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"owner","type":"bytes32"}],"name":"removeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newOwner","type":"bytes32"}],"name":"resetOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"newOwners","type":"bytes32[]"}],"name":"resetOwners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fallbackContract","type":"address"}],"name":"setFallbackHandler","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"hookAddress","type":"address"}],"name":"uninstallHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"moduleAddress","type":"address"}],"name":"uninstallModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"validator","type":"address"}],"name":"uninstallValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oldImplementation","type":"address"}],"name":"upgradeFrom","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes32","name":"accountGasLimits","type":"bytes32"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"bytes32","name":"gasFees","type":"bytes32"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct PackedUserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"},{"internalType":"uint256","name":"missingAccountFunds","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"validationData","type":"uint256"}],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.