Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
There are no matching entriesUpdate your filters to view other transactions | |||||||||
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
ModularAccount
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 50000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {
IModularAccount, ValidationConfig
} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";
import {ExecutionInstallDelegate} from "../helpers/ExecutionInstallDelegate.sol";
import {ModularAccountBase} from "./ModularAccountBase.sol";
/// @title Modular Account
/// @author Alchemy
/// @notice This contract allows initializing with a validation config (of a validation module) to be installed on
/// the account.
contract ModularAccount is ModularAccountBase {
constructor(IEntryPoint entryPoint, ExecutionInstallDelegate executionInstallDelegate)
ModularAccountBase(entryPoint, executionInstallDelegate)
{}
/// @notice Initializes the account with a validation function.
/// @dev This function is only callable once.
function initializeWithValidation(
ValidationConfig validationConfig,
bytes4[] calldata selectors,
bytes calldata installData,
bytes[] calldata hooks
) external virtual initializer {
_installValidation(validationConfig, selectors, installData, hooks);
}
/// @inheritdoc IModularAccount
function accountId() external pure override returns (string memory) {
return "alchemy.modular-account.2.0.0";
}
/// @dev Overrides ModularAccountView.
function _isNativeFunction(uint32 selector) internal pure override returns (bool) {
return super._isNativeFunction(selector) || selector == uint32(this.initializeWithValidation.selector);
}
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import {ExecutionManifest} from "./IExecutionModule.sol";
type ModuleEntity is bytes24;
// ModuleEntity is a packed representation of a module function
// Layout:
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________________ // Address
// 0x________________________________________BBBBBBBB________________ // Entity ID
// 0x________________________________________________0000000000000000 // unused
type ValidationConfig is bytes25;
// ValidationConfig is a packed representation of a validation function and flags for its configuration.
// Layout:
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________________ // Address
// 0x________________________________________BBBBBBBB________________ // Entity ID
// 0x________________________________________________CC______________ // ValidationFlags
// 0x__________________________________________________00000000000000 // unused
type ValidationFlags is uint8;
// ValidationFlags layout:
// 0b00000___ // unused
// 0b_____A__ // isGlobal
// 0b______B_ // isSignatureValidation
// 0b_______C // isUserOpValidation
type HookConfig is bytes25;
// HookConfig is a packed representation of a hook function and flags for its configuration.
// Layout:
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________________ // Address
// 0x________________________________________BBBBBBBB________________ // Entity ID
// 0x________________________________________________CC______________ // Hook Flags
//
// Hook flags layout:
// 0b00000___ // unused
// 0b_____A__ // hasPre (exec only)
// 0b______B_ // hasPost (exec only)
// 0b_______C // hook type (0 for exec, 1 for validation)
struct Call {
// The target address for the account to call.
address target;
// The value to send with the call.
uint256 value;
// The calldata for the call.
bytes data;
}
interface IModularAccount {
event ExecutionInstalled(address indexed module, ExecutionManifest manifest);
event ExecutionUninstalled(address indexed module, bool onUninstallSucceeded, ExecutionManifest manifest);
event ValidationInstalled(address indexed module, uint32 indexed entityId);
event ValidationUninstalled(address indexed module, uint32 indexed entityId, bool onUninstallSucceeded);
/// @notice Standard execute method.
/// @param target The target address for the account to call.
/// @param value The value to send with the call.
/// @param data The calldata for the call.
/// @return The return data from the call.
function execute(address target, uint256 value, bytes calldata data) external payable returns (bytes memory);
/// @notice Standard executeBatch method.
/// @dev If the target is a module, the call SHOULD revert. If any of the calls revert, the entire batch MUST
/// revert.
/// @param calls The array of calls.
/// @return An array containing the return data from the calls.
function executeBatch(Call[] calldata calls) external payable returns (bytes[] memory);
/// @notice Execute a call using the specified runtime validation.
/// @param data The calldata to send to the account.
/// @param authorization The authorization data to use for the call. The first 24 bytes is a ModuleEntity which
/// specifies which runtime validation to use, and the rest is sent as a parameter to runtime validation.
function executeWithRuntimeValidation(bytes calldata data, bytes calldata authorization)
external
payable
returns (bytes memory);
/// @notice Install a module to the modular account.
/// @param module The module to install.
/// @param manifest the manifest describing functions to install.
/// @param installData Optional data to be used by the account to handle the initial execution setup. Data
/// encoding
/// is implementation-specific.
function installExecution(address module, ExecutionManifest calldata manifest, bytes calldata installData)
external;
/// @notice Uninstall a module from the modular account.
/// @param module The module to uninstall.
/// @param manifest the manifest describing functions to uninstall.
/// @param uninstallData Optional data to be used by the account to handle the execution uninstallation. Data
/// encoding is implementation-specific.
function uninstallExecution(address module, ExecutionManifest calldata manifest, bytes calldata uninstallData)
external;
/// @notice Installs a validation function across a set of execution selectors, and optionally mark it as a
/// global validation function.
/// @dev This does not validate anything against the manifest - the caller must ensure validity.
/// @param validationConfig The validation function to install, along with configuration flags.
/// @param selectors The selectors to install the validation function for.
/// @param installData Optional data to be used by the account to handle the initial validation setup. Data
/// encoding is implementation-specific.
/// @param hooks Optional hooks to install and associate with the validation function. Data encoding is
/// implementation-specific.
function installValidation(
ValidationConfig validationConfig,
bytes4[] calldata selectors,
bytes calldata installData,
bytes[] calldata hooks
) external;
/// @notice Uninstall a validation function from a set of execution selectors.
/// @param validationFunction The validation function to uninstall.
/// @param uninstallData Optional data to be used by the account to handle the validation uninstallation. Data
/// encoding is implementation-specific.
/// @param hookUninstallData Optional data to be used by the account to handle hook uninstallation. Data
/// encoding
/// is implementation-specific.
function uninstallValidation(
ModuleEntity validationFunction,
bytes calldata uninstallData,
bytes[] calldata hookUninstallData
) external;
/// @notice Return a unique identifier for the account implementation.
/// @dev This function MUST return a string in the format "vendor.account.semver". The vendor and account
/// names MUST NOT contain a period character.
/// @return The account ID.
function accountId() external view returns (string memory);
}/**
** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
** Only one instance required on each chain.
**/
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
import "./PackedUserOperation.sol";
import "./IStakeManager.sol";
import "./IAggregator.sol";
import "./INonceManager.sol";
interface IEntryPoint is IStakeManager, INonceManager {
/***
* An event emitted after each successful request.
* @param userOpHash - Unique identifier for the request (hash its entire content, except signature).
* @param sender - The account that generates this request.
* @param paymaster - If non-null, the paymaster that pays for this request.
* @param nonce - The nonce value from the request.
* @param success - True if the sender transaction succeeded, false if reverted.
* @param actualGasCost - Actual amount paid (by account or paymaster) for this UserOperation.
* @param actualGasUsed - Total gas used by this UserOperation (including preVerification, creation,
* validation and execution).
*/
event UserOperationEvent(
bytes32 indexed userOpHash,
address indexed sender,
address indexed paymaster,
uint256 nonce,
bool success,
uint256 actualGasCost,
uint256 actualGasUsed
);
/**
* Account "sender" was deployed.
* @param userOpHash - The userOp that deployed this account. UserOperationEvent will follow.
* @param sender - The account that is deployed
* @param factory - The factory used to deploy this account (in the initCode)
* @param paymaster - The paymaster used by this UserOp
*/
event AccountDeployed(
bytes32 indexed userOpHash,
address indexed sender,
address factory,
address paymaster
);
/**
* An event emitted if the UserOperation "callData" reverted with non-zero length.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
* @param revertReason - The return bytes from the (reverted) call to "callData".
*/
event UserOperationRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* An event emitted if the UserOperation Paymaster's "postOp" call reverted with non-zero length.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
* @param revertReason - The return bytes from the (reverted) call to "callData".
*/
event PostOpRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
*/
event UserOperationPrefundTooLow(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce
);
/**
* An event emitted by handleOps(), before starting the execution loop.
* Any event emitted before this event, is part of the validation.
*/
event BeforeExecution();
/**
* Signature aggregator used by the following UserOperationEvents within this bundle.
* @param aggregator - The aggregator used for the following UserOperationEvents.
*/
event SignatureAggregatorChanged(address indexed aggregator);
/**
* A custom revert error of handleOps, to identify the offending op.
* Should be caught in off-chain handleOps simulation and not happen on-chain.
* Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
* NOTE: If simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
* @param reason - Revert reason. The string starts with a unique code "AAmn",
* where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
* so a failure can be attributed to the correct entity.
*/
error FailedOp(uint256 opIndex, string reason);
/**
* A custom revert error of handleOps, to report a revert by account or paymaster.
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
* @param reason - Revert reason. see FailedOp(uint256,string), above
* @param inner - data from inner cought revert reason
* @dev note that inner is truncated to 2048 bytes
*/
error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
error PostOpReverted(bytes returnData);
/**
* Error case when a signature aggregator fails to verify the aggregated signature it had created.
* @param aggregator The aggregator that failed to verify the signature
*/
error SignatureValidationFailed(address aggregator);
// Return value of getSenderAddress.
error SenderAddressResult(address sender);
// UserOps handled, per aggregator.
struct UserOpsPerAggregator {
PackedUserOperation[] userOps;
// Aggregator address
IAggregator aggregator;
// Aggregated signature
bytes signature;
}
/**
* Execute a batch of UserOperations.
* No signature aggregator is used.
* If any account requires an aggregator (that is, it returned an aggregator when
* performing simulateValidation), then handleAggregatedOps() must be used instead.
* @param ops - The operations to execute.
* @param beneficiary - The address to receive the fees.
*/
function handleOps(
PackedUserOperation[] calldata ops,
address payable beneficiary
) external;
/**
* Execute a batch of UserOperation with Aggregators
* @param opsPerAggregator - The operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts).
* @param beneficiary - The address to receive the fees.
*/
function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
address payable beneficiary
) external;
/**
* Generate a request Id - unique identifier for this request.
* The request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
* @param userOp - The user operation to generate the request ID for.
* @return hash the hash of this UserOperation
*/
function getUserOpHash(
PackedUserOperation calldata userOp
) external view returns (bytes32);
/**
* Gas and return values during simulation.
* @param preOpGas - The gas used for validation (including preValidationGas)
* @param prefund - The required prefund for this operation
* @param accountValidationData - returned validationData from account.
* @param paymasterValidationData - return validationData from paymaster.
* @param paymasterContext - Returned by validatePaymasterUserOp (to be passed into postOp)
*/
struct ReturnInfo {
uint256 preOpGas;
uint256 prefund;
uint256 accountValidationData;
uint256 paymasterValidationData;
bytes paymasterContext;
}
/**
* Returned aggregated signature info:
* The aggregator returned by the account, and its current stake.
*/
struct AggregatorStakeInfo {
address aggregator;
StakeInfo stakeInfo;
}
/**
* Get counterfactual sender address.
* Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
* This method always revert, and returns the address in SenderAddressResult error
* @param initCode - The constructor code to be passed into the UserOperation.
*/
function getSenderAddress(bytes memory initCode) external;
error DelegateAndRevert(bool success, bytes ret);
/**
* Helper method for dry-run testing.
* @dev calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result.
* The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace
* actual EntryPoint code is less convenient.
* @param target a target contract to make a delegatecall from entrypoint
* @param data data to pass to target in a delegatecall
*/
function delegateAndRevert(address target, bytes calldata data) external;
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {
ExecutionManifest,
ManifestExecutionHook
} from "@erc6900/reference-implementation/interfaces/IExecutionModule.sol";
import {HookConfig, IModularAccount} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {IModule} from "@erc6900/reference-implementation/interfaces/IModule.sol";
import {HookConfigLib} from "@erc6900/reference-implementation/libraries/HookConfigLib.sol";
import {AccountStorage, ExecutionStorage, getAccountStorage, toSetValue} from "../account/AccountStorage.sol";
import {KnownSelectorsLib} from "../libraries/KnownSelectorsLib.sol";
import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol";
import {ModuleInstallCommonsLib} from "../libraries/ModuleInstallCommonsLib.sol";
/// @title Execution Install Delegate
/// @author Alchemy
/// @notice This contract acts as an external library which is meant to handle execution function installations and
/// uninstallations via delegatecall.
contract ExecutionInstallDelegate {
using LinkedListSetLib for LinkedListSet;
address internal immutable _THIS_ADDRESS;
error ERC4337FunctionNotAllowed(bytes4 selector);
error ExecutionFunctionAlreadySet(bytes4 selector);
error ExecutionFunctionNotSet(bytes4 selector);
error ExecutionHookNotSet(HookConfig hookConfig);
error IModuleFunctionNotAllowed(bytes4 selector);
error NullModule();
error OnlyDelegateCall();
modifier onlyDelegateCall() {
if (address(this) == _THIS_ADDRESS) {
revert OnlyDelegateCall();
}
_;
}
constructor() {
_THIS_ADDRESS = address(this);
}
// External Functions
/// @notice Update components according to the manifest.
function installExecution(
address module,
ExecutionManifest calldata manifest,
bytes calldata moduleInstallData
) external onlyDelegateCall {
AccountStorage storage _storage = getAccountStorage();
if (module == address(0)) {
revert NullModule();
}
// Update components according to the manifest.
uint256 length = manifest.executionFunctions.length;
for (uint256 i = 0; i < length; ++i) {
bytes4 selector = manifest.executionFunctions[i].executionSelector;
bool skipRuntimeValidation = manifest.executionFunctions[i].skipRuntimeValidation;
bool allowGlobalValidation = manifest.executionFunctions[i].allowGlobalValidation;
_setExecutionFunction(selector, skipRuntimeValidation, allowGlobalValidation, module);
}
length = manifest.executionHooks.length;
for (uint256 i = 0; i < length; ++i) {
ManifestExecutionHook memory mh = manifest.executionHooks[i];
LinkedListSet storage executionHooks = _storage.executionStorage[mh.executionSelector].executionHooks;
HookConfig hookConfig = HookConfigLib.packExecHook({
_module: module,
_entityId: mh.entityId,
_hasPre: mh.isPreHook,
_hasPost: mh.isPostHook
});
ModuleInstallCommonsLib.addExecHooks(executionHooks, hookConfig);
}
length = manifest.interfaceIds.length;
for (uint256 i = 0; i < length; ++i) {
_storage.supportedIfaces[manifest.interfaceIds[i]] += 1;
}
ModuleInstallCommonsLib.onInstall(module, moduleInstallData, type(IModule).interfaceId);
emit IModularAccount.ExecutionInstalled(module, manifest);
}
/// @notice Remove components according to the manifest, in reverse order (by component type) of their
/// installation.
function uninstallExecution(address module, ExecutionManifest calldata manifest, bytes calldata uninstallData)
external
onlyDelegateCall
{
AccountStorage storage _storage = getAccountStorage();
if (module == address(0)) {
revert NullModule();
}
uint256 length = manifest.executionHooks.length;
for (uint256 i = 0; i < length; ++i) {
ManifestExecutionHook memory mh = manifest.executionHooks[i];
LinkedListSet storage executionHooks = _storage.executionStorage[mh.executionSelector].executionHooks;
HookConfig hookConfig = HookConfigLib.packExecHook({
_module: module,
_entityId: mh.entityId,
_hasPre: mh.isPreHook,
_hasPost: mh.isPostHook
});
_removeExecHooks(executionHooks, hookConfig);
}
length = manifest.executionFunctions.length;
for (uint256 i = 0; i < length; ++i) {
bytes4 selector = manifest.executionFunctions[i].executionSelector;
_removeExecutionFunction(selector);
}
length = manifest.interfaceIds.length;
for (uint256 i = 0; i < length; ++i) {
_storage.supportedIfaces[manifest.interfaceIds[i]] -= 1;
}
// Clear the module storage for the account.
bool onUninstallSuccess = ModuleInstallCommonsLib.onUninstall(module, uninstallData);
emit IModularAccount.ExecutionUninstalled(module, onUninstallSuccess, manifest);
}
// Private Functions
function _setExecutionFunction(
bytes4 selector,
bool skipRuntimeValidation,
bool allowGlobalValidation,
address module
) internal {
ExecutionStorage storage _executionStorage = getAccountStorage().executionStorage[selector];
if (_executionStorage.module != address(0)) {
revert ExecutionFunctionAlreadySet(selector);
}
// Note that there is no check for native function selectors. Installing a function with a colliding
// selector will lead to the installed function being unreachable.
// Make sure incoming execution function is not a function in IModule
if (KnownSelectorsLib.isIModuleFunction(uint32(selector))) {
revert IModuleFunctionNotAllowed(selector);
}
// Also make sure it doesn't collide with functions defined by ERC-4337 and called by the entry point. This
// prevents a malicious module from sneaking in a function with the same selector as e.g.
// `validatePaymasterUserOp` and turning the account into their own personal paymaster.
if (KnownSelectorsLib.isERC4337Function(uint32(selector))) {
revert ERC4337FunctionNotAllowed(selector);
}
_executionStorage.module = module;
_executionStorage.skipRuntimeValidation = skipRuntimeValidation;
_executionStorage.allowGlobalValidation = allowGlobalValidation;
}
function _removeExecutionFunction(bytes4 selector) internal {
ExecutionStorage storage _executionStorage = getAccountStorage().executionStorage[selector];
if (_executionStorage.module == address(0)) {
revert ExecutionFunctionNotSet(selector);
}
_executionStorage.module = address(0);
_executionStorage.skipRuntimeValidation = false;
_executionStorage.allowGlobalValidation = false;
}
function _removeExecHooks(LinkedListSet storage hooks, HookConfig hookConfig) internal {
if (!hooks.tryRemove(toSetValue(hookConfig))) {
revert ExecutionHookNotSet(hookConfig);
}
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {getEmptyCalldataSlice} from "@erc6900/reference-implementation/helpers/EmptyCalldataSlice.sol";
import {ExecutionManifest} from "@erc6900/reference-implementation/interfaces/IExecutionModule.sol";
import {
Call,
HookConfig,
IModularAccount,
ModuleEntity,
ValidationConfig,
ValidationFlags
} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {HookConfig, HookConfigLib} from "@erc6900/reference-implementation/libraries/HookConfigLib.sol";
import {ModuleEntityLib} from "@erc6900/reference-implementation/libraries/ModuleEntityLib.sol";
import {SparseCalldataSegmentLib} from "@erc6900/reference-implementation/libraries/SparseCalldataSegmentLib.sol";
import {ValidationConfigLib} from "@erc6900/reference-implementation/libraries/ValidationConfigLib.sol";
import {IAccountExecute} from "@eth-infinitism/account-abstraction/interfaces/IAccountExecute.sol";
import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol";
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {UUPSUpgradeable} from "solady/utils/UUPSUpgradeable.sol";
import {ExecutionInstallDelegate} from "../helpers/ExecutionInstallDelegate.sol";
import {_coalescePreValidation, _coalesceValidation} from "../helpers/ValidationResHelpers.sol";
import {IModularAccountBase} from "../interfaces/IModularAccountBase.sol";
import {
DensePostHookData,
ExecutionLib,
PHCallBuffer,
RTCallBuffer,
SigCallBuffer,
UOCallBuffer
} from "../libraries/ExecutionLib.sol";
import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol";
import {MemManagementLib, MemSnapshot} from "../libraries/MemManagementLib.sol";
import {
ValidationLocator, ValidationLocatorLib, ValidationLookupKey
} from "../libraries/ValidationLocatorLib.sol";
import {AccountBase} from "./AccountBase.sol";
import {AccountStorage, ValidationStorage, getAccountStorage, toSetValue} from "./AccountStorage.sol";
import {AccountStorageInitializable} from "./AccountStorageInitializable.sol";
import {ModularAccountView} from "./ModularAccountView.sol";
import {ModuleManagerInternals} from "./ModuleManagerInternals.sol";
import {TokenReceiver} from "./TokenReceiver.sol";
/// @title Modular Account Base
/// @author Alchemy
/// @notice This abstract contract is a modular account that is compliant with ERC-6900 standard. It supports
/// deferred actions during validation.
abstract contract ModularAccountBase is
IModularAccount,
IModularAccountBase,
ModularAccountView,
AccountStorageInitializable,
AccountBase,
IERC1271,
IERC165,
IAccountExecute,
ModuleManagerInternals,
UUPSUpgradeable,
TokenReceiver
{
using LinkedListSetLib for LinkedListSet;
using ModuleEntityLib for ModuleEntity;
using ValidationConfigLib for ValidationFlags;
using HookConfigLib for HookConfig;
using SparseCalldataSegmentLib for bytes;
enum ValidationCheckingType {
GLOBAL,
SELECTOR,
EITHER
}
// keccak256("EIP712Domain(uint256 chainId,address verifyingContract)")
bytes32 internal constant _DOMAIN_SEPARATOR_TYPEHASH =
0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
// keccak256("DeferredAction(uint256 nonce,uint48 deadline,bytes call)")
bytes32 internal constant _DEFERRED_ACTION_TYPEHASH =
0x9b23e06584efc6b65fc854cee55011d89f86485487b6db36aed7d23884711ea3;
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 internal constant _INTERFACE_ID_INVALID = 0xffffffff;
// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e;
bytes4 internal constant _1271_INVALID = 0xffffffff;
address internal immutable _EXECUTION_INSTALL_DELEGATE;
error CreateFailed();
error DeferredActionSignatureInvalid();
error RequireUserOperationContext();
error SelfCallRecursionDepthExceeded();
error SignatureValidationInvalid(ModuleEntity validationFunction);
error UserOpValidationInvalid(ModuleEntity validationFunction);
error UnexpectedAggregator(ModuleEntity validationFunction, address aggregator);
error UnrecognizedFunction(bytes4 selector);
error ValidationFunctionMissing(bytes4 selector);
error DeferredValidationHasValidationHooks();
// Wraps execution of a native function with runtime validation and hooks
// Used for performCreate, execute, executeBatch, installExecution, uninstallExecution, installValidation,
// uninstallValidation, upgradeToAndCall, updateFallbackSignerData.
modifier wrapNativeFunction() {
DensePostHookData postHookData = _checkPermittedCallerAndAssociatedHooks();
_;
ExecutionLib.doCachedPostHooks(postHookData);
}
constructor(IEntryPoint entryPoint, ExecutionInstallDelegate executionInstallDelegate)
AccountBase(entryPoint)
{
_disableInitializers();
_EXECUTION_INSTALL_DELEGATE = address(executionInstallDelegate);
}
// EXTERNAL FUNCTIONS
receive() external payable {}
/// @notice Fallback function
/// @dev Routes calls to execution functions based on the incoming msg.sig. If there's no module associated
/// with this function selector, revert.
///
/// @return The raw returned data from the invoked execution function.
fallback(bytes calldata) external payable returns (bytes memory) {
address execModule = getAccountStorage().executionStorage[msg.sig].module;
if (execModule == address(0)) {
revert UnrecognizedFunction(msg.sig);
}
DensePostHookData postHookData = _checkPermittedCallerAndAssociatedHooks();
// execute the function, bubbling up any reverts
ExecutionLib.callBubbleOnRevertTransient(execModule, 0 wei, msg.data);
bytes memory execReturnData = ExecutionLib.collectReturnData();
ExecutionLib.doCachedPostHooks(postHookData);
return execReturnData;
}
/// @inheritdoc IModularAccountBase
function performCreate(uint256 value, bytes calldata initCode, bool isCreate2, bytes32 salt)
external
payable
virtual
override
wrapNativeFunction
returns (address createdAddr)
{
assembly ("memory-safe") {
// Load the free memory pointer.
let fmp := mload(0x40)
// Get the initCode length.
let len := initCode.length
// Copy the initCode from callata to memory at the free memory pointer.
calldatacopy(fmp, initCode.offset, len)
switch isCreate2
case 1 { createdAddr := create2(value, fmp, len, salt) }
default { createdAddr := create(value, fmp, len) }
if iszero(createdAddr) {
// If creation failed (the address returned is zero), revert with CreateFailed().
mstore(0x00, 0x7e16b8cd)
revert(0x1c, 0x04)
}
}
}
/// @inheritdoc IAccountExecute
/// @notice Execution function that allows UO context to be passed to execution hooks
/// @dev This function is only callable by the EntryPoint
function executeUserOp(PackedUserOperation calldata userOp, bytes32) external override {
_requireFromEntryPoint();
ValidationLocator locator = ValidationLocatorLib.loadFromNonce(userOp.nonce);
HookConfig[] memory validationAssocExecHooks =
MemManagementLib.loadExecHooks(getAccountStorage().validationStorage[locator.lookupKey()]);
PHCallBuffer callBuffer;
if (validationAssocExecHooks.length > 0) {
callBuffer = ExecutionLib.allocatePreExecHookCallBuffer(msg.data);
}
DensePostHookData postHookData = ExecutionLib.doPreHooks(validationAssocExecHooks, callBuffer);
bytes memory callData = ExecutionLib.getExecuteUOCallData(callBuffer, userOp.callData);
// Manually call self, without collecting return data unless there's a revert.
ExecutionLib.callBubbleOnRevert(address(this), 0, callData);
ExecutionLib.doCachedPostHooks(postHookData);
}
/// @inheritdoc IModularAccount
/// @notice May be validated by a global validation.
function execute(address target, uint256 value, bytes calldata data)
external
payable
override
wrapNativeFunction
returns (bytes memory result)
{
ExecutionLib.callBubbleOnRevertTransient(target, value, data);
// Only return data if not called by the EntryPoint
if (msg.sender != address(_ENTRY_POINT)) {
result = ExecutionLib.collectReturnData();
}
}
/// @inheritdoc IModularAccount
/// @notice May be validated by a global validation function.
function executeBatch(Call[] calldata calls)
external
payable
override
wrapNativeFunction
returns (bytes[] memory results)
{
uint256 callsLength = calls.length;
if (msg.sender != address(_ENTRY_POINT)) {
results = new bytes[](callsLength);
for (uint256 i = 0; i < callsLength; ++i) {
ExecutionLib.callBubbleOnRevertTransient(calls[i].target, calls[i].value, calls[i].data);
results[i] = ExecutionLib.collectReturnData();
}
} else {
for (uint256 i = 0; i < callsLength; ++i) {
ExecutionLib.callBubbleOnRevertTransient(calls[i].target, calls[i].value, calls[i].data);
}
}
}
/// @inheritdoc IModularAccount
function executeWithRuntimeValidation(bytes calldata data, bytes calldata authorization)
external
payable
returns (bytes memory)
{
(ValidationLocator locator, bytes calldata authorizationData) =
ValidationLocatorLib.loadFromSignature(authorization);
ValidationStorage storage _validationStorage = getAccountStorage().validationStorage[locator.lookupKey()];
// Check if the runtime validation function is allowed to be called
_checkIfValidationAppliesCallData(
data,
locator.lookupKey(),
// Unfortunately, have to avoid declaring a `bool isGlobalValidation` to avoid stack too deep issues.
locator.isGlobal() ? ValidationCheckingType.GLOBAL : ValidationCheckingType.SELECTOR
);
RTCallBuffer rtCallBuffer = _doRuntimeValidation(locator.lookupKey(), data, authorizationData);
// If runtime validation passes, run exec hooks associated with the validator
HookConfig[] memory validationAssocExecHooks = MemManagementLib.loadExecHooks(_validationStorage);
PHCallBuffer phCallBuffer;
if (validationAssocExecHooks.length > 0) {
phCallBuffer = ExecutionLib.convertToPreHookCallBuffer(rtCallBuffer, data);
}
DensePostHookData postHookData = ExecutionLib.doPreHooks(validationAssocExecHooks, phCallBuffer);
// Execute the call, reusing the already-allocated RT call buffers, if it exists.
// In practice, this is cheaper than attempting to coalesce the (possibly two) buffers.
ExecutionLib.executeRuntimeSelfCall(rtCallBuffer, data);
bytes memory returnData = ExecutionLib.collectReturnData();
ExecutionLib.doCachedPostHooks(postHookData);
return returnData;
}
/// @inheritdoc IModularAccount
/// @notice May be validated by a global validation.
function installExecution(
address module,
ExecutionManifest calldata manifest,
bytes calldata moduleInstallData
) external override wrapNativeFunction {
// Access params to prevent compiler unused parameter flags.
(module, manifest, moduleInstallData);
address delegate = _EXECUTION_INSTALL_DELEGATE;
ExecutionLib.delegatecallBubbleOnRevertTransient(delegate);
}
/// @inheritdoc IModularAccount
/// @notice May be validated by a global validation.
function uninstallExecution(
address module,
ExecutionManifest calldata manifest,
bytes calldata moduleUninstallData
) external override wrapNativeFunction {
// Access params to prevent compiler unused parameter flags.
(module, manifest, moduleUninstallData);
address delegate = _EXECUTION_INSTALL_DELEGATE;
ExecutionLib.delegatecallBubbleOnRevertTransient(delegate);
}
/// @inheritdoc IModularAccount
/// @notice May be validated by a global validation.
/// @dev This function can be used to update (to a certain degree) previously installed validation functions.
/// - preValidationHook, executionHooks, and selectors can be added later. Though they won't be deleted.
/// - isGlobal and isSignatureValidation can also be updated later.
function installValidation(
ValidationConfig validationConfig,
bytes4[] calldata selectors,
bytes calldata installData,
bytes[] calldata hooks
) external virtual wrapNativeFunction {
_installValidation(validationConfig, selectors, installData, hooks);
}
/// @inheritdoc IModularAccount
/// @notice May be validated by a global validation.
function uninstallValidation(
ModuleEntity validationFunction,
bytes calldata uninstallData,
bytes[] calldata hookUninstallData
) external wrapNativeFunction {
_uninstallValidation(validationFunction, uninstallData, hookUninstallData);
}
/// @inheritdoc IERC1271
function isValidSignature(bytes32 hash, bytes calldata signature) external view override returns (bytes4) {
(ValidationLocator locator, bytes calldata signatureRemainder) =
ValidationLocatorLib.loadFromSignature(signature);
return _isValidSignature(locator.lookupKey(), hash, signatureRemainder);
}
/// @inheritdoc IERC165
/// @notice ERC-165 introspection
/// @dev returns true for `IERC165.interfaceId` and false for `0xFFFFFFFF`
/// @param interfaceId interface id to check against
/// @return bool support for specific interface
function supportsInterface(bytes4 interfaceId) external view override returns (bool) {
if (interfaceId == _INTERFACE_ID_INVALID) {
return false;
}
if (
interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IERC1155Receiver).interfaceId
|| interfaceId == type(IERC165).interfaceId
) {
return true;
}
return getAccountStorage().supportedIfaces[interfaceId] > 0;
}
/// @inheritdoc IModularAccount
function accountId() external pure virtual returns (string memory);
/// @inheritdoc UUPSUpgradeable
/// @notice May be validated by a global validation.
function upgradeToAndCall(address newImplementation, bytes calldata data)
public
payable
virtual
override
onlyProxy
wrapNativeFunction
{
super.upgradeToAndCall(newImplementation, data);
}
// INTERNAL FUNCTIONS
// Parent function validateUserOp enforces that this call can only be made by the EntryPoint
function _validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
internal
override
returns (uint256 validationData)
{
ValidationLocator locator = ValidationLocatorLib.loadFromNonce(userOp.nonce);
bytes calldata userOpSignature = userOp.signature;
/// The calldata layout is unique for deferred validation installation.
/// Byte indices are [inclusive, exclusive] and relative to the start of the signature after the locator is
/// decoded and removed.
/// [0:4] : uint32, encodedDatalength.
/// [4:(4 + encodedDatalength)] : bytes, abi-encoded deferred action data.
/// [(4 + encodedDataLength):(8 + encodedDataLength)] : uint32, deferredActionSigLength.
/// [(8 + encodedDataLength):(8 + deferredActionSigLength + encodedDataLength)] : bytes,
/// deferred action sig. This is the signature passed to the outer validation decoded earlier.
/// [(8 + deferredActionSigLength + encodedDataLength):] : bytes, userOpSignature. This is the
/// signature passed to the inner validation.
if (locator.hasDeferredAction()) {
// Use inner validation as a 1271 validation for the deferred action, then use the outer
// validation to validate the UO.
// Get the length of the deferred action data.
uint256 encodedDataLength = uint32(bytes4(userOpSignature[:4]));
// Load the pointer to the encoded data.
bytes calldata encodedData = userOpSignature[4:4 + encodedDataLength];
// Get the deferred action signature length.
uint256 deferredActionSigLength =
uint32(bytes4(userOpSignature[4 + encodedDataLength:8 + encodedDataLength]));
// Get the deferred installation signature, which is passed to the outer validation to handle the
// deferred action.
bytes calldata deferredActionSig =
userOpSignature[8 + encodedDataLength:8 + encodedDataLength + deferredActionSigLength];
//Validate the signature.
// Freeze the free-memory pointer, since we won't need to use anything from the deferred action
// validation memory.
MemSnapshot memSnapshot = MemManagementLib.freezeFMP();
uint48 deadline = _handleDeferredAction(userOp.nonce, encodedData, deferredActionSig);
// Restore the free memory pointer.
MemManagementLib.restoreFMP(memSnapshot);
// Update the validation data with the deadline.
validationData = uint256(deadline) << 160;
// Update the UserOp signature to the remaining bytes.
userOpSignature = userOpSignature[8 + encodedDataLength + deferredActionSigLength:];
}
_checkIfValidationAppliesCallData(
userOp.callData,
locator.lookupKey(),
locator.isGlobal() ? ValidationCheckingType.GLOBAL : ValidationCheckingType.SELECTOR
);
// Check if there are execution hooks associated with the validator, and revert if the call isn't to
// `executeUserOp`. This check must be here because if context isn't passed, we can't tell in execution
// which hooks should have ran.
if (
getAccountStorage().validationStorage[locator.lookupKey()].executionHookCount > 0
&& bytes4(userOp.callData[:4]) != this.executeUserOp.selector
) {
revert RequireUserOperationContext();
}
uint256 userOpValidationRes = _doUserOpValidation(userOp, userOpHash, locator.lookupKey(), userOpSignature);
// We only coalesce validations if the validation data from deferred installation is nonzero.
if (validationData != 0) {
validationData = _coalesceValidation(validationData, userOpValidationRes);
} else {
validationData = userOpValidationRes;
}
}
/// @return The deadline of the deferred action
function _handleDeferredAction(uint256 userOpNonce, bytes calldata encodedData, bytes calldata sig)
internal
returns (uint48)
{
// The inner validation, deadline, and deferred call bytes are all at fixed positions in the encoded data.
// [:21] = ValidationLocator defActionValidationLocator
// [21:27] = uint48 deadline
// [27:] = bytes deferredCall
ValidationLocator defActionValidationLocator = ValidationLocator.wrap(uint168(bytes21(encodedData[:21])));
ValidationStorage storage _validationStorage =
getAccountStorage().validationStorage[defActionValidationLocator.lookupKey()];
// Because this bypasses UO validation hooks, we require that the validation used does not include any
// validation hooks.
if (_validationStorage.validationHookCount != 0) {
revert DeferredValidationHasValidationHooks();
}
uint48 deadline = uint48(bytes6(encodedData[21:27]));
bytes32 typedDataHash = _computeDeferredActionHash(userOpNonce, deadline, encodedData[27:]);
// Check if the outer validation applies to the function call
_checkIfValidationAppliesCallData(
encodedData[27:],
defActionValidationLocator.lookupKey(),
defActionValidationLocator.isGlobal() ? ValidationCheckingType.GLOBAL : ValidationCheckingType.SELECTOR
);
// Handle the signature validation
_validateDeferredActionSignature(typedDataHash, sig, defActionValidationLocator.lookupKey());
// Run the validation associated execution hooks, allocating a call buffer as needed.
HookConfig[] memory validationAssocExecHooks = MemManagementLib.loadExecHooks(_validationStorage);
PHCallBuffer callBuffer;
if (validationAssocExecHooks.length > 0) {
callBuffer = ExecutionLib.allocatePreExecHookCallBuffer(encodedData[27:]);
}
DensePostHookData postHookData = ExecutionLib.doPreHooks(validationAssocExecHooks, callBuffer);
// Perform the deferred action's self call on the account.
ExecutionLib.callBubbleOnRevertTransient(address(this), 0, encodedData[27:]);
// Do the cached post hooks
ExecutionLib.doCachedPostHooks(postHookData);
return deadline;
}
// To support gas estimation, we don't fail early when the failure is caused by a signature failure
function _doUserOpValidation(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
ValidationLookupKey validationLookupKey,
bytes calldata signature
) internal returns (uint256) {
ValidationStorage storage _validationStorage = getAccountStorage().validationStorage[validationLookupKey];
// Do preUserOpValidation hooks
HookConfig[] memory preUserOpValidationHooks = MemManagementLib.loadValidationHooks(_validationStorage);
uint256 validationRes;
UOCallBuffer userOpCallBuffer;
if (!_validationIsNative(validationLookupKey) || preUserOpValidationHooks.length > 0) {
userOpCallBuffer = ExecutionLib.allocateUserOpValidationCallBuffer(userOp, userOpHash);
}
bytes calldata currentSignatureSlice;
for (uint256 i = preUserOpValidationHooks.length; i > 0; i) {
// Decrement here, instead of in the loop body, to convert from length to an index.
unchecked {
--i;
}
(currentSignatureSlice, signature) =
signature.advanceSegmentIfAtIndex(uint8(preUserOpValidationHooks.length - i - 1));
ModuleEntity uoValidationHook = preUserOpValidationHooks[i].moduleEntity();
uint256 currentValidationRes =
ExecutionLib.invokeUserOpCallBuffer(userOpCallBuffer, uoValidationHook, currentSignatureSlice);
if (uint160(currentValidationRes) > 1) {
// If the aggregator is not 0 or 1, it is an unexpected value
revert UnexpectedAggregator(uoValidationHook, address(uint160(currentValidationRes)));
}
validationRes = _coalescePreValidation(validationRes, currentValidationRes);
}
// Run the user op validation function
{
currentSignatureSlice = signature.getFinalSegment();
uint256 currentValidationRes =
_execUserOpValidation(validationLookupKey, userOpHash, currentSignatureSlice, userOpCallBuffer);
if (preUserOpValidationHooks.length != 0) {
// If we have other validation data we need to coalesce with
validationRes = _coalesceValidation(validationRes, currentValidationRes);
} else {
validationRes = currentValidationRes;
}
}
return validationRes;
}
function _doRuntimeValidation(
ValidationLookupKey validationLookupKey,
bytes calldata callData,
bytes calldata authorizationData
) internal returns (RTCallBuffer) {
ValidationStorage storage _validationData = getAccountStorage().validationStorage[validationLookupKey];
// run all preRuntimeValidation hooks
HookConfig[] memory preRuntimeValidationHooks = MemManagementLib.loadValidationHooks(_validationData);
RTCallBuffer callBuffer;
if (!_validationIsNative(validationLookupKey) || preRuntimeValidationHooks.length > 0) {
callBuffer = ExecutionLib.allocateRuntimeValidationCallBuffer(callData, authorizationData);
}
for (uint256 i = preRuntimeValidationHooks.length; i > 0;) {
// Decrement here, instead of in the loop update step, to handle the case where the length is 0.
unchecked {
--i;
}
bytes calldata currentAuthSegment;
(currentAuthSegment, authorizationData) =
authorizationData.advanceSegmentIfAtIndex(uint8(preRuntimeValidationHooks.length - i - 1));
ExecutionLib.invokeRuntimeCallBufferPreValidationHook(
callBuffer, preRuntimeValidationHooks[i], currentAuthSegment
);
}
authorizationData = authorizationData.getFinalSegment();
_execRuntimeValidation(validationLookupKey, callBuffer, authorizationData);
return callBuffer;
}
// solhint-disable-next-line no-empty-blocks
function _authorizeUpgrade(address newImplementation) internal override {}
/**
* Order of operations:
* 1. Check if the sender is the entry point, the account itself, or the selector called is public.
* - Yes: Return an empty array, there are no post executionHooks.
* - No: Continue
* 2. Check if the called selector (msg.sig) is included in the set of selectors the msg.sender can
* directly call.
* - Yes: Continue
* - No: Revert, the caller is not allowed to call this selector
* 3. If there are runtime validation hooks associated with this caller-sig combination, run them.
* 4. Run the pre executionHooks associated with this caller-sig combination, and return the
* post executionHooks to run later.
*/
function _checkPermittedCallerAndAssociatedHooks() internal returns (DensePostHookData) {
AccountStorage storage _storage = getAccountStorage();
HookConfig[] memory execHooks;
RTCallBuffer rtCallBuffer;
// We only need to handle execution hooks when the sender is not the entry point or the account itself,
// and the selector isn't public.
if (
msg.sender != address(_ENTRY_POINT) && msg.sender != address(this)
&& !_storage.executionStorage[msg.sig].skipRuntimeValidation
) {
ValidationLookupKey directCallValidationKey = ValidationLocatorLib.directCallLookupKey(msg.sender);
_checkIfValidationAppliesCallData(msg.data, directCallValidationKey, ValidationCheckingType.EITHER);
// Direct call is allowed, run associated execution & validation hooks
// Validation hooks
HookConfig[] memory preRuntimeValidationHooks =
MemManagementLib.loadValidationHooks(_storage.validationStorage[directCallValidationKey]);
uint256 preRuntimeValidationHooksLength = preRuntimeValidationHooks.length;
if (preRuntimeValidationHooksLength > 0) {
rtCallBuffer = ExecutionLib.allocateRuntimeValidationCallBuffer(msg.data, getEmptyCalldataSlice());
}
for (uint256 i = preRuntimeValidationHooksLength; i > 0;) {
// Decrement here, instead of in the loop body, to convert from length to an index.
unchecked {
--i;
}
ExecutionLib.invokeRuntimeCallBufferPreValidationHook(
rtCallBuffer, preRuntimeValidationHooks[i], getEmptyCalldataSlice()
);
}
//Load all execution hooks: both associated with the selector and the validation function.
execHooks = MemManagementLib.loadExecHooks(
_storage.executionStorage[msg.sig], _storage.validationStorage[directCallValidationKey]
);
} else {
// If the sender is the entry point or the account itself, or the selector is public, this indicates
// that validation was done elsewhere. We only need to run selector-associated execution hooks.
execHooks = MemManagementLib.loadExecHooks(_storage.executionStorage[msg.sig]);
}
PHCallBuffer preHookCallBuffer;
if (execHooks.length > 0) {
preHookCallBuffer = ExecutionLib.convertToPreHookCallBuffer(rtCallBuffer, msg.data);
}
// Exec hooks associated with the selector
DensePostHookData postHookData = ExecutionLib.doPreHooks(execHooks, preHookCallBuffer);
return postHookData;
}
function _execUserOpValidation(
ValidationLookupKey validationLookupKey,
bytes32 hash,
bytes calldata signatureSegment,
UOCallBuffer callBuffer
) internal virtual returns (uint256) {
(hash); // unused in ModularAccountBase, but used in SemiModularAccountBase
ValidationStorage storage _validationStorage = getAccountStorage().validationStorage[validationLookupKey];
ModuleEntity userOpValidationFunction = validationLookupKey.moduleEntity(_validationStorage);
if (!_validationStorage.validationFlags.isUserOpValidation()) {
revert UserOpValidationInvalid(userOpValidationFunction);
}
ExecutionLib.convertToValidationBuffer(callBuffer);
return ExecutionLib.invokeUserOpCallBuffer(callBuffer, userOpValidationFunction, signatureSegment);
}
function _execRuntimeValidation(
ValidationLookupKey validationLookupKey,
RTCallBuffer callBuffer,
bytes calldata authorization
) internal virtual {
ValidationStorage storage _validationData = getAccountStorage().validationStorage[validationLookupKey];
ModuleEntity runtimeValidationFunction = validationLookupKey.moduleEntity(_validationData);
ExecutionLib.invokeRuntimeCallBufferValidation(callBuffer, runtimeValidationFunction, authorization);
}
function _computeDeferredActionHash(uint256 userOpNonce, uint48 deadline, bytes calldata selfCall)
internal
view
returns (bytes32)
{
// Note:
// - A zero deadline translates to "no deadline"
// - The user op nonce also includes the data for:
// - Which validation function to use
// - Whether or not a deferred action is included
// - Whether or not the validation is used as a global validation.
// Compute the hash without permanently allocating memory for each step.
// The following is equivalent to:
// keccak256(
// abi.encode(
// _DEFERRED_ACTION_TYPEHASH,
// nonce,
// deadline,
// keccak256(selfCall)
// )
// )
// Compute the struct hash, then convert it to a typed data hash.
bytes32 structHash;
assembly ("memory-safe") {
// Get the hash of the dynamic-length encoded install call
let fmp := mload(0x40)
calldatacopy(fmp, selfCall.offset, selfCall.length)
let selfCallHash := keccak256(fmp, selfCall.length)
// Compute the struct hash
let ptr := fmp
mstore(ptr, _DEFERRED_ACTION_TYPEHASH)
ptr := add(ptr, 0x20)
mstore(ptr, userOpNonce)
ptr := add(ptr, 0x20)
// Clear the upper bits of the deadline, in case the caller didn't.
mstore(ptr, and(deadline, 0xffffffffffff))
ptr := add(ptr, 0x20)
mstore(ptr, selfCallHash)
// Compute the struct hash
structHash := keccak256(fmp, 0x80)
}
bytes32 typedDataHash = MessageHashUtils.toTypedDataHash(_domainSeparator(), structHash);
return typedDataHash;
}
function _validateDeferredActionSignature(
bytes32 defActionTypedDataHash,
bytes calldata signature,
ValidationLookupKey deferredSigValidationLookupKey
) internal view {
// Validate the 1271 signature.
SigCallBuffer sigCallBuffer;
if (!_validationIsNative(deferredSigValidationLookupKey)) {
sigCallBuffer = ExecutionLib.allocateSigCallBuffer(defActionTypedDataHash, signature);
}
if (
_exec1271Validation(sigCallBuffer, defActionTypedDataHash, deferredSigValidationLookupKey, signature)
!= _1271_MAGIC_VALUE
) {
revert DeferredActionSignatureInvalid();
}
}
function _isValidSignature(ValidationLookupKey validationLookupKey, bytes32 hash, bytes calldata signature)
internal
view
returns (bytes4)
{
ValidationStorage storage _validationStorage = getAccountStorage().validationStorage[validationLookupKey];
HookConfig[] memory preSignatureValidationHooks = MemManagementLib.loadValidationHooks(_validationStorage);
SigCallBuffer sigCallBuffer;
if (!_validationIsNative(validationLookupKey) || preSignatureValidationHooks.length > 0) {
sigCallBuffer = ExecutionLib.allocateSigCallBuffer(hash, signature);
}
for (uint256 i = preSignatureValidationHooks.length; i > 0;) {
// Decrement here, instead of in the loop body, to convert from length to an index.
unchecked {
--i;
}
bytes calldata currentSignatureSegment;
(currentSignatureSegment, signature) =
signature.advanceSegmentIfAtIndex(uint8(preSignatureValidationHooks.length - i - 1));
ExecutionLib.invokePreSignatureValidationHook(
sigCallBuffer, preSignatureValidationHooks[i], currentSignatureSegment
);
}
signature = signature.getFinalSegment();
return _exec1271Validation(sigCallBuffer, hash, validationLookupKey, signature);
}
function _exec1271Validation(
SigCallBuffer buffer,
bytes32 hash,
ValidationLookupKey validationLookupKey,
bytes calldata signatureSegment
) internal view virtual returns (bytes4) {
(hash); // unused in ModularAccountBase, but used in SemiModularAccountBase
ValidationStorage storage _validationStorage = getAccountStorage().validationStorage[validationLookupKey];
ModuleEntity sigValidation = validationLookupKey.moduleEntity(_validationStorage);
if (!_validationStorage.validationFlags.isSignatureValidation()) {
revert SignatureValidationInvalid(sigValidation);
}
if (ExecutionLib.invokeSignatureValidation(buffer, sigValidation, signatureSegment) == _1271_MAGIC_VALUE) {
return _1271_MAGIC_VALUE;
}
return _1271_INVALID;
}
function _isValidationGlobal(ValidationLookupKey validationFunction) internal view virtual returns (bool) {
return getAccountStorage().validationStorage[validationFunction].validationFlags.isGlobal();
}
function _checkIfValidationAppliesCallData(
bytes calldata callData,
ValidationLookupKey validationFunction,
ValidationCheckingType checkingType
) internal view {
if (callData.length < 4) {
revert UnrecognizedFunction(bytes4(callData));
}
bytes4 outerSelector = bytes4(callData);
if (outerSelector == this.executeUserOp.selector) {
// If the selector is executeUserOp, pull the actual selector from the following data,
// and trim the calldata to ensure the self-call decoding is still accurate.
callData = callData[4:];
outerSelector = bytes4(callData[:4]);
}
_checkIfValidationAppliesSelector(outerSelector, validationFunction, checkingType);
if (outerSelector == IModularAccount.execute.selector) {
address target = MemManagementLib.getExecuteTarget(callData);
if (target == address(this)) {
// There is no point to call `execute` to recurse exactly once - this is equivalent to just having
// the calldata as a top-level call.
revert SelfCallRecursionDepthExceeded();
}
} else if (outerSelector == IModularAccount.executeBatch.selector) {
// executeBatch may be used to batch account actions together, by targetting the account itself.
// If this is done, we must ensure all of the inner calls are allowed by the provided validation
// function.
_checkExecuteBatchValidationApplicability(callData[4:], validationFunction, checkingType);
}
}
/// @notice Checks if the validation function is allowed to perform this call to `executeBatch`.
/// @param callData The calldata to check, excluding the `executeBatch` selector.
/// @param validationFunction The validation function to check against.
/// @param checkingType The type of validation checking to perform.
function _checkExecuteBatchValidationApplicability(
bytes calldata callData,
ValidationLookupKey validationFunction,
ValidationCheckingType checkingType
) internal view {
// Equivalent to the following code, but without using memory.
// (Call[] memory calls) = abi.decode(callData, (Call[]));
// for (uint256 i = 0; i < calls.length; ++i) {
// if (calls[i].target == address(this)) {
// bytes4 nestedSelector = bytes4(calls[i].data[:4]);
// if (
// nestedSelector == IModularAccount.execute.selector
// || nestedSelector == IModularAccount.executeBatch.selector
// ) {
//
// revert SelfCallRecursionDepthExceeded();
// }
// _checkIfValidationAppliesSelector(nestedSelector, validationFunction, checkingType);
// }
// }
// The following is adapted from the compiler-generated ABI decoder for the `Call[] calldata` parameter
// type. See test/mocks/MockDecoder.sol for more info.
// This allows the decoding behavior here, in the validation step, to match what would happen during the
// actual execution of `executeBatch`.
// This follows the compiler-generated behavior of:
// - asserting the data to load fits in the remaining space of the current `bytes calldata`.
// - asserting that the ABI-encoded offsets and lengths do not exceed the constant value
// 0xffffffffffffffff.
// The end of allowed calldata to read. Declared in an outer context to make available to multiple code
// blocks.
uint256 dataEnd;
// The absolute offset of the start of the `Call[]` array.
uint256 arrayPos;
// The length of the `Call[]` array.
uint256 callsLength;
// This block is retrieving the actual Call[] location and length, asserting it doesn't go out of bounds.
assembly ("memory-safe") {
// Set up the "safe data decoding range"
let headStart := callData.offset
dataEnd := add(headStart, callData.length)
// Assert it is safe to load the offset
if slt(sub(dataEnd, headStart), 32) { revert(0, 0) }
// Load and sanitize the offset
let relOffset := calldataload(callData.offset)
if gt(relOffset, 0xffffffffffffffff) { revert(0, 0) }
// Convert from a relative offset to an absolute offset.
let absOffset := add(headStart, relOffset)
// Assert it is safe to load the length
if iszero(slt(add(absOffset, 0x1f), dataEnd)) { revert(0, 0) }
// Load and sanitize the length
callsLength := calldataload(absOffset)
if gt(callsLength, 0xffffffffffffffff) { revert(0, 0) }
// Load the array position, and check that it fits within the alloted length.
arrayPos := add(absOffset, 0x20)
if gt(add(arrayPos, mul(callsLength, 0x20)), dataEnd) { revert(0, 0) }
}
// Now, we have the array length and data bounds.
// Iterate through the array elements, checking:
// - If the target is this account, assert that:
// - the selector in the data field is not `execute` or `executeBatch`.
// - the provided validation is allowed to call the selector.
for (uint256 i = 0; i < callsLength; ++i) {
address callTarget;
uint256 structAbsOffset;
// This block is retrieving the actual calls[i] struct location and contents, asserting it doesn't go
// out of bounds.
assembly ("memory-safe") {
// Load and sanitize the struct offset.
// This is still safe to load, from the bounds check above.
let structRelOffset := calldataload(add(arrayPos, mul(i, 0x20)))
if gt(structRelOffset, 0xffffffffffffffff) { revert(0, 0) }
// Validate struct offset. If the offset points to a location with < 3 words of space before the
// end of data, revert.
if iszero(slt(structRelOffset, sub(sub(dataEnd, arrayPos), sub(0x60, 1)))) { revert(0, 0) }
structAbsOffset := add(arrayPos, structRelOffset)
// Load the address from the struct, and sanitize its contents, to mirror the behavior of the ABI
// decoder.
callTarget := calldataload(structAbsOffset)
if iszero(eq(and(callTarget, 0xffffffffffffffffffffffffffffffffffffffff), callTarget)) {
revert(0, 0)
}
}
if (callTarget == address(this)) {
// In this case, we must load the selector, deny if it's `execute` or `executeBatch`, and check
// validation applicability.
uint32 selector;
// This block is retrieving the selector from the first 4 bytes of calls[i].data, asserting it
// doesn't go out of bounds and that the data is at least 4 bytes long.
assembly ("memory-safe") {
// Load and sanitize the data offset.
let dataRelOffset := calldataload(add(structAbsOffset, 0x40))
if gt(dataRelOffset, 0xffffffffffffffff) { revert(0, 0) }
// Validate data offset. If the offset points to a location with < 1 words of space before the
// end of data, revert.
if iszero(slt(dataRelOffset, sub(sub(dataEnd, structAbsOffset), sub(0x20, 1)))) {
revert(0, 0)
}
let dataAbsOffset := add(structAbsOffset, dataRelOffset)
// Load and sanitize the data length.
let dataLength := calldataload(dataAbsOffset)
if gt(dataLength, 0xffffffff) { revert(0, 0) }
// Get the data offset, and assert that the following data fits into the bounded calldata
// range.
let dataOffset := add(dataAbsOffset, 0x20)
if sgt(dataOffset, sub(dataEnd, mul(dataLength, 0x01))) { revert(0, 0) }
// Finally, load the selector being called. This will be the first 4 bytes of the data.
// If the data length is less than 4, revert.
if slt(dataLength, 4) { revert(0, 0) }
selector := shr(224, calldataload(dataOffset))
}
if (selector == uint32(this.execute.selector) || selector == uint32(this.executeBatch.selector)) {
// To prevent arbitrarily-deep recursive checking, we limit the depth of self-calls to one
// for the purposes of batching.
// This means that all self-calls must occur at the top level of the batch.
// Note that modules of other contracts using `executeWithRuntimeValidation` may still
// independently call into this account with a different validation function, allowing
// composition of multiple batches.
revert SelfCallRecursionDepthExceeded();
}
_checkIfValidationAppliesSelector(bytes4(selector), validationFunction, checkingType);
}
}
}
function _checkIfValidationAppliesSelector(
bytes4 selector,
ValidationLookupKey validationFunction,
ValidationCheckingType checkingType
) internal view {
// Check that the provided validation function is applicable to the selector
if (checkingType == ValidationCheckingType.GLOBAL) {
if (!_globalValidationApplies(selector, validationFunction)) {
revert ValidationFunctionMissing(selector);
}
} else if (checkingType == ValidationCheckingType.SELECTOR) {
if (!_selectorValidationApplies(selector, validationFunction)) {
revert ValidationFunctionMissing(selector);
}
} else {
if (
!_globalValidationApplies(selector, validationFunction)
&& !_selectorValidationApplies(selector, validationFunction)
) {
revert ValidationFunctionMissing(selector);
}
}
}
function _globalValidationApplies(bytes4 selector, ValidationLookupKey validationFunction)
internal
view
returns (bool)
{
return _globalValidationAllowed(selector) && _isValidationGlobal(validationFunction);
}
function _globalValidationAllowed(bytes4 selector) internal view returns (bool) {
return _isGlobalValidationAllowedNativeFunction(uint32(selector))
|| getAccountStorage().executionStorage[selector].allowGlobalValidation;
}
function _selectorValidationApplies(bytes4 selector, ValidationLookupKey validationFunction)
internal
view
returns (bool)
{
return getAccountStorage().validationStorage[validationFunction].selectors.contains(toSetValue(selector));
}
function _domainSeparator() internal view returns (bytes32) {
bytes32 result;
// Compute the hash without permanently allocating memory
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, _DOMAIN_SEPARATOR_TYPEHASH)
mstore(add(fmp, 0x20), chainid())
mstore(add(fmp, 0x40), address())
result := keccak256(fmp, 0x60)
}
return result;
}
// A virtual function to detect if a validation function is natively implemented. Used for determining call
// buffer allocation.
function _validationIsNative(ValidationLookupKey) internal pure virtual returns (bool) {
return false;
}
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import {IModule} from "./IModule.sol";
struct ManifestExecutionFunction {
// The selector to install
bytes4 executionSelector;
// If true, the function won't need runtime validation, and can be called by anyone.
bool skipRuntimeValidation;
// If true, the function can be validated by a global validation function.
bool allowGlobalValidation;
}
struct ManifestExecutionHook {
bytes4 executionSelector;
uint32 entityId;
bool isPreHook;
bool isPostHook;
}
/// @dev A struct describing how the module should be installed on a modular account.
struct ExecutionManifest {
// Execution functions defined in this module to be installed on the MSCA.
ManifestExecutionFunction[] executionFunctions;
ManifestExecutionHook[] executionHooks;
// List of ERC-165 interface IDs to add to account to support introspection checks. This MUST NOT include
// IModule's interface ID.
bytes4[] interfaceIds;
}
interface IExecutionModule is IModule {
/// @notice Describe the contents and intended configuration of the module.
/// @dev This manifest MUST stay constant over time.
/// @return A manifest describing the contents and intended configuration of the module.
function executionManifest() external pure returns (ExecutionManifest memory);
}// 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: GPL-3.0-only
pragma solidity >=0.7.5;
/**
* Manage deposits and stakes.
* Deposit is just a balance used to pay for UserOperations (either by a paymaster or an account).
* Stake is value locked for at least "unstakeDelay" by the staked entity.
*/
interface IStakeManager {
event Deposited(address indexed account, uint256 totalDeposit);
event Withdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
// Emitted when stake or unstake delay are modified.
event StakeLocked(
address indexed account,
uint256 totalStaked,
uint256 unstakeDelaySec
);
// Emitted once a stake is scheduled for withdrawal.
event StakeUnlocked(address indexed account, uint256 withdrawTime);
event StakeWithdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
/**
* @param deposit - The entity's deposit.
* @param staked - True if this entity is staked.
* @param stake - Actual amount of ether staked for this entity.
* @param unstakeDelaySec - Minimum delay to withdraw the stake.
* @param withdrawTime - First block timestamp where 'withdrawStake' will be callable, or zero if already locked.
* @dev Sizes were chosen so that deposit fits into one cell (used during handleOp)
* and the rest fit into a 2nd cell (used during stake/unstake)
* - 112 bit allows for 10^15 eth
* - 48 bit for full timestamp
* - 32 bit allows 150 years for unstake delay
*/
struct DepositInfo {
uint256 deposit;
bool staked;
uint112 stake;
uint32 unstakeDelaySec;
uint48 withdrawTime;
}
// API struct used by getStakeInfo and simulateValidation.
struct StakeInfo {
uint256 stake;
uint256 unstakeDelaySec;
}
/**
* Get deposit info.
* @param account - The account to query.
* @return info - Full deposit information of given account.
*/
function getDepositInfo(
address account
) external view returns (DepositInfo memory info);
/**
* Get account balance.
* @param account - The account to query.
* @return - The deposit (for gas payment) of the account.
*/
function balanceOf(address account) external view returns (uint256);
/**
* Add to the deposit of the given account.
* @param account - The account to add to.
*/
function depositTo(address account) external payable;
/**
* Add to the account's stake - amount and delay
* any pending unstake is first cancelled.
* @param _unstakeDelaySec - The new lock duration before the deposit can be withdrawn.
*/
function addStake(uint32 _unstakeDelaySec) external payable;
/**
* Attempt to unlock the stake.
* The value can be withdrawn (using withdrawStake) after the unstake delay.
*/
function unlockStake() external;
/**
* Withdraw from the (unlocked) stake.
* Must first call unlockStake and wait for the unstakeDelay to pass.
* @param withdrawAddress - The address to send withdrawn value.
*/
function withdrawStake(address payable withdrawAddress) external;
/**
* Withdraw from the deposit.
* @param withdrawAddress - The address to send withdrawn value.
* @param withdrawAmount - The amount to withdraw.
*/
function withdrawTo(
address payable withdrawAddress,
uint256 withdrawAmount
) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
import "./PackedUserOperation.sol";
/**
* Aggregated Signatures validator.
*/
interface IAggregator {
/**
* Validate aggregated signature.
* Revert if the aggregated signature does not match the given list of operations.
* @param userOps - Array of UserOperations to validate the signature for.
* @param signature - The aggregated signature.
*/
function validateSignatures(
PackedUserOperation[] calldata userOps,
bytes calldata signature
) external view;
/**
* Validate signature of a single userOp.
* This method should be called by bundler after EntryPointSimulation.simulateValidation() returns
* the aggregator this account uses.
* First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
* @param userOp - The userOperation received from the user.
* @return sigForUserOp - The value to put into the signature field of the userOp when calling handleOps.
* (usually empty, unless account and aggregator support some kind of "multisig".
*/
function validateUserOpSignature(
PackedUserOperation calldata userOp
) external view returns (bytes memory sigForUserOp);
/**
* Aggregate multiple signatures into a single value.
* This method is called off-chain to calculate the signature to pass with handleOps()
* bundler MAY use optimized custom code perform this aggregation.
* @param userOps - Array of UserOperations to collect the signatures from.
* @return aggregatedSignature - The aggregated signature.
*/
function aggregateSignatures(
PackedUserOperation[] calldata userOps
) external view returns (bytes memory aggregatedSignature);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
interface INonceManager {
/**
* Return the next nonce for this sender.
* Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop)
* But UserOp with different keys can come with arbitrary order.
*
* @param sender the account address
* @param key the high 192 bit of the nonce
* @return nonce a full nonce to pass for next UserOp with this sender.
*/
function getNonce(address sender, uint192 key)
external view returns (uint256 nonce);
/**
* Manually increment the nonce of the sender.
* This method is exposed just for completeness..
* Account does NOT need to call it, neither during validation, nor elsewhere,
* as the EntryPoint will update the nonce regardless.
* Possible use-case is call it with various keys to "initialize" their nonces to one, so that future
* UserOperations will not pay extra for the first transaction with a given key.
*/
function incrementNonce(uint192 key) external;
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
interface IModule is IERC165 {
/// @notice Initialize module data for the modular account.
/// @dev Called by the modular account during `installExecution`.
/// @param data Optional bytes array to be decoded and used by the module to setup initial module data for the
/// modular account.
function onInstall(bytes calldata data) external;
/// @notice Clear module data for the modular account.
/// @dev Called by the modular account during `uninstallExecution`.
/// @param data Optional bytes array to be decoded and used by the module to clear module data for the modular
/// account.
function onUninstall(bytes calldata data) external;
/// @notice Return a unique identifier for the module.
/// @dev This function MUST return a string in the format "vendor.module.semver". The vendor and module
/// names MUST NOT contain a period character.
/// @return The module ID.
function moduleId() external view returns (string memory);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {HookConfig, ModuleEntity} from "../interfaces/IModularAccount.sol";
// Hook types:
// Exec hook: bools for hasPre, hasPost
// Validation hook: no bools
// Hook fields:
// module address
// entity ID
// hook type
// if exec hook: hasPre, hasPost
// Hook config is a packed representation of a hook function and flags for its configuration.
// Layout:
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________________ // Address
// 0x________________________________________BBBBBBBB________________ // Entity ID
// 0x________________________________________________CC______________ // Hook Flags
// Hook flags layout:
// 0b00000___ // unused
// 0b_____A__ // hasPre (exec only)
// 0b______B_ // hasPost (exec only)
// 0b_______C // hook type (0 for exec, 1 for validation)
library HookConfigLib {
// Hook type constants
// Exec has no bits set
bytes32 internal constant _HOOK_TYPE_EXEC = bytes32(uint256(0));
// Validation has 1 in the 25th byte
bytes32 internal constant _HOOK_TYPE_VALIDATION = bytes32(uint256(1) << 56);
// Exec hook flags constants
// Pre hook has 1 in 4's bit in the 25th byte
bytes32 internal constant _EXEC_HOOK_HAS_PRE = bytes32(uint256(1) << 58);
// Post hook has 1 in 2's bit in the 25th byte
bytes32 internal constant _EXEC_HOOK_HAS_POST = bytes32(uint256(1) << 57);
function packValidationHook(ModuleEntity _hookFunction) internal pure returns (HookConfig) {
return
HookConfig.wrap(bytes25(bytes25(ModuleEntity.unwrap(_hookFunction)) | bytes25(_HOOK_TYPE_VALIDATION)));
}
function packValidationHook(address _module, uint32 _entityId) internal pure returns (HookConfig) {
return HookConfig.wrap(
bytes25(
// module address stored in the first 20 bytes
bytes25(bytes20(_module))
// entityId stored in the 21st - 24th byte
| bytes25(bytes24(uint192(_entityId))) | bytes25(_HOOK_TYPE_VALIDATION)
)
);
}
function packExecHook(ModuleEntity _hookFunction, bool _hasPre, bool _hasPost)
internal
pure
returns (HookConfig)
{
return HookConfig.wrap(
bytes25(
bytes25(ModuleEntity.unwrap(_hookFunction))
// | bytes25(_HOOK_TYPE_EXEC) // Can omit because exec type is 0
| bytes25(_hasPre ? _EXEC_HOOK_HAS_PRE : bytes32(0))
| bytes25(_hasPost ? _EXEC_HOOK_HAS_POST : bytes32(0))
)
);
}
function packExecHook(address _module, uint32 _entityId, bool _hasPre, bool _hasPost)
internal
pure
returns (HookConfig)
{
return HookConfig.wrap(
bytes25(
// module address stored in the first 20 bytes
bytes25(bytes20(_module))
// entityId stored in the 21st - 24th byte
| bytes25(bytes24(uint192(_entityId)))
// | bytes25(_HOOK_TYPE_EXEC) // Can omit because exec type is 0
| bytes25(_hasPre ? _EXEC_HOOK_HAS_PRE : bytes32(0))
| bytes25(_hasPost ? _EXEC_HOOK_HAS_POST : bytes32(0))
)
);
}
function unpackValidationHook(HookConfig _config) internal pure returns (ModuleEntity _hookFunction) {
bytes25 configBytes = HookConfig.unwrap(_config);
_hookFunction = ModuleEntity.wrap(bytes24(configBytes));
}
function unpackExecHook(HookConfig _config)
internal
pure
returns (ModuleEntity _hookFunction, bool _hasPre, bool _hasPost)
{
bytes25 configBytes = HookConfig.unwrap(_config);
_hookFunction = ModuleEntity.wrap(bytes24(configBytes));
_hasPre = configBytes & _EXEC_HOOK_HAS_PRE != 0;
_hasPost = configBytes & _EXEC_HOOK_HAS_POST != 0;
}
function module(HookConfig _config) internal pure returns (address) {
return address(bytes20(HookConfig.unwrap(_config)));
}
function entityId(HookConfig _config) internal pure returns (uint32) {
return uint32(bytes4(HookConfig.unwrap(_config) << 160));
}
function moduleEntity(HookConfig _config) internal pure returns (ModuleEntity) {
return ModuleEntity.wrap(bytes24(HookConfig.unwrap(_config)));
}
// Check if the hook is a validation hook
// If false, it is an exec hook
function isValidationHook(HookConfig _config) internal pure returns (bool) {
return HookConfig.unwrap(_config) & _HOOK_TYPE_VALIDATION != 0;
}
// Check if the exec hook has a pre hook
// Undefined behavior if the hook is not an exec hook
function hasPreHook(HookConfig _config) internal pure returns (bool) {
return HookConfig.unwrap(_config) & _EXEC_HOOK_HAS_PRE != 0;
}
// Check if the exec hook has a post hook
// Undefined behavior if the hook is not an exec hook
function hasPostHook(HookConfig _config) internal pure returns (bool) {
return HookConfig.unwrap(_config) & _EXEC_HOOK_HAS_POST != 0;
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {HookConfig, ValidationFlags} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {LinkedListSet, SetValue} from "../libraries/LinkedListSetLib.sol";
import {ValidationLookupKey} from "../libraries/ValidationLocatorLib.sol";
// ERC-7201 derived storage slot.
// keccak256(abi.encode(uint256(keccak256("Alchemy.ModularAccount.Storage_V2")) - 1)) & ~bytes32(uint256(0xff))
bytes32 constant _ACCOUNT_STORAGE_SLOT = 0x596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd00;
/// @notice Represents data associated with a specific function selector.
struct ExecutionStorage {
// The module that implements this execution function.
// If this is a native function, the address should remain address(0).
address module;
// Whether or not the function needs runtime validation, or can be called without any validation. The function
// can still be state changing if this flag is set to true.
// Note that even if this is set to true, user op validation will still be required, otherwise any caller could
// drain the account of native tokens by wasting gas.
bool skipRuntimeValidation;
// Whether or not a global validation function may be used to validate this function.
bool allowGlobalValidation;
// The execution hooks for this function selector.
LinkedListSet executionHooks;
}
/// @notice Represents data associated with a specific validation function.
struct ValidationStorage {
// The address of the validation module.
address module;
// ValidationFlags layout:
// 0b00000___ // unused
// 0b_____A__ // isGlobal
// 0b______B_ // isSignatureValidation
// 0b_______C // isUserOpValidation
ValidationFlags validationFlags;
// Length of the validation hooks for this validation function. The length is stored here, in the same storage
// slot as the flags, to save an `sload` when putting the hooks into memory.
uint8 validationHookCount;
// Length of execution hooks for this validation function. The length is stored here, in the same storage slot
// as the flags, to save an `sload` when putting the hooks into memory.
uint8 executionHookCount;
// The validation hooks for this validation function.
LinkedListSet validationHooks;
// Execution hooks to run with this validation function.
LinkedListSet executionHooks;
// The set of selectors that may be validated by this validation function.
LinkedListSet selectors;
}
/// @custom:storage-location erc7201:Alchemy.ModularAccount.Storage_V2
struct AccountStorage {
// AccountStorageInitializable variables.
uint64 initialized;
bool initializing;
// Execution functions and their associated functions.
mapping(bytes4 selector => ExecutionStorage) executionStorage;
// Validation functions and their associated functions.
mapping(ValidationLookupKey lookupKey => ValidationStorage) validationStorage;
// Module-defined ERC-165 interfaces installed on the account.
mapping(bytes4 => uint256) supportedIfaces;
}
function getAccountStorage() pure returns (AccountStorage storage _storage) {
assembly ("memory-safe") {
_storage.slot := _ACCOUNT_STORAGE_SLOT
}
}
function toSetValue(HookConfig hookConfig) pure returns (SetValue) {
return SetValue.wrap(bytes31(HookConfig.unwrap(hookConfig)));
}
function toSetValue(bytes4 selector) pure returns (SetValue) {
return SetValue.wrap(bytes31(selector));
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {IExecutionHookModule} from "@erc6900/reference-implementation/interfaces/IExecutionHookModule.sol";
import {IExecutionModule} from "@erc6900/reference-implementation/interfaces/IExecutionModule.sol";
import {IModule} from "@erc6900/reference-implementation/interfaces/IModule.sol";
import {IValidationHookModule} from "@erc6900/reference-implementation/interfaces/IValidationHookModule.sol";
import {IValidationModule} from "@erc6900/reference-implementation/interfaces/IValidationModule.sol";
import {IAggregator} from "@eth-infinitism/account-abstraction/interfaces/IAggregator.sol";
import {IPaymaster} from "@eth-infinitism/account-abstraction/interfaces/IPaymaster.sol";
/// @title Known Selectors Library
/// @author Alchemy
/// @notice Library to help to check if a selector is an ERC-6900 module function or a an ERC-4337 contract
/// function.
library KnownSelectorsLib {
/// @notice Check if a selector is an ERC-4337 function.
/// @param selector The selector to check.
/// @return True if the selector is an ERC-4337 function, false otherwise.
function isERC4337Function(uint32 selector) internal pure returns (bool) {
return selector == uint32(IAggregator.validateSignatures.selector)
|| selector == uint32(IAggregator.validateUserOpSignature.selector)
|| selector == uint32(IAggregator.aggregateSignatures.selector)
|| selector == uint32(IPaymaster.validatePaymasterUserOp.selector)
|| selector == uint32(IPaymaster.postOp.selector);
}
/// @notice Check if a selector is an ERC-6900 module function.
/// @param selector The selector to check.
/// @return True if the selector is an ERC-6900 module function, false otherwise.
function isIModuleFunction(uint32 selector) internal pure returns (bool) {
return selector == uint32(IModule.onInstall.selector) || selector == uint32(IModule.onUninstall.selector)
|| selector == uint32(IModule.moduleId.selector)
|| selector == uint32(IExecutionModule.executionManifest.selector)
|| selector == uint32(IExecutionHookModule.preExecutionHook.selector)
|| selector == uint32(IExecutionHookModule.postExecutionHook.selector)
|| selector == uint32(IValidationModule.validateUserOp.selector)
|| selector == uint32(IValidationModule.validateRuntime.selector)
|| selector == uint32(IValidationModule.validateSignature.selector)
|| selector == uint32(IValidationHookModule.preUserOpValidationHook.selector)
|| selector == uint32(IValidationHookModule.preRuntimeValidationHook.selector)
|| selector == uint32(IValidationHookModule.preSignatureValidationHook.selector);
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
type SetValue is bytes31;
/// @dev The sentinel value is used to indicate the head and tail of the list.
bytes32 constant SENTINEL_VALUE = bytes32(uint256(1));
struct LinkedListSet {
// Byte Layout
// | value | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA__ |
// | meta | 0x______________________________________________________________BB |
// Bit-layout of the meta byte
// | unused | 0b0000000_ |
// | sentinel | 0b_______A |
// Key excludes the meta bytes, except for the sentinel value, which is 0x1
mapping(bytes32 => bytes32) map;
}
/// @title Linked List Set Library
/// @author Alchemy
/// @notice This library provides a set of functions for managing enumerable sets of bytes31 values. It is a fork
/// of the LinkedListSet library in modular-account-libs, with the following changes:
/// - The flags feature has been removed, so the library no longer supports both the "has next" flag, and the
/// user-defined flags.
/// - The library has been modified to work with bytes31 values instead of bytes30 values.
library LinkedListSetLib {
/// @notice Add a value to a set.
/// @param set The set to add the value to.
/// @param value The value to add.
/// @return True if the value was added, false if the value cannot be added (already exists or is zero).
function tryAdd(LinkedListSet storage set, SetValue value) internal returns (bool) {
mapping(bytes32 => bytes32) storage map = set.map;
bytes32 unwrappedKey = SetValue.unwrap(value);
if (unwrappedKey == bytes32(0) || map[unwrappedKey] != bytes32(0)) return false;
bytes32 prev = map[SENTINEL_VALUE];
if (prev == bytes32(0) || isSentinel(prev)) {
// Set is empty
map[SENTINEL_VALUE] = unwrappedKey;
map[unwrappedKey] = SENTINEL_VALUE;
} else {
// set is not empty
map[SENTINEL_VALUE] = unwrappedKey;
map[unwrappedKey] = prev;
}
return true;
}
/// @notice Remove a value from a set.
/// @dev This is an O(n) operation, where n is the number of elements in the set.
/// @param set The set to remove the value from.
/// @param value The value to remove.
/// @return True if the value was removed, false if the value does not exist.
function tryRemove(LinkedListSet storage set, SetValue value) internal returns (bool) {
mapping(bytes32 => bytes32) storage map = set.map;
bytes32 unwrappedKey = SetValue.unwrap(value);
bytes32 nextValue = map[unwrappedKey];
if (unwrappedKey == bytes32(0) || nextValue == bytes32(0)) return false;
bytes32 prevKey = SENTINEL_VALUE;
bytes32 currentKey;
do {
currentKey = map[prevKey];
if (currentKey == unwrappedKey) {
// Set the previous value's next value to the next value,
// and the flags to the current value's flags.
// and the next value's `hasNext` flag to determine whether or not the next value is (or points to)
// the sentinel value.
map[prevKey] = nextValue;
map[currentKey] = bytes32(0);
return true;
}
prevKey = currentKey;
} while (!isSentinel(currentKey) && currentKey != bytes32(0));
return false;
}
/// @notice Remove a value from a set, given the previous value in the set.
/// @dev This is an O(1) operation but requires additional knowledge.
/// @param set The set to remove the value from.
/// @param value The value to remove.
/// @param prev The previous value in the set.
/// @return True if the value was removed, false if the value does not exist, or if the wrong prev was
/// specified.
function tryRemoveKnown(LinkedListSet storage set, SetValue value, bytes32 prev) internal returns (bool) {
mapping(bytes32 => bytes32) storage map = set.map;
bytes32 unwrappedKey = SetValue.unwrap(value);
if (prev == bytes32(0) || unwrappedKey == bytes32(0)) {
return false;
}
// assert that the previous value's next value is the value to be removed
bytes32 currentValue = map[prev];
if (currentValue != unwrappedKey) {
return false;
}
bytes32 next = map[unwrappedKey];
if (next == bytes32(0)) {
// The set didn't actually contain the value
return false;
}
map[prev] = next;
map[unwrappedKey] = bytes32(0);
return true;
}
/// @notice Remove all values from a set.
/// @dev This is an O(n) operation, where n is the number of elements in the set.
/// @param set The set to remove the values from.
function clear(LinkedListSet storage set) internal {
mapping(bytes32 => bytes32) storage map = set.map;
bytes32 cursor = SENTINEL_VALUE;
do {
bytes32 next = map[cursor];
map[cursor] = bytes32(0);
cursor = next;
} while (!isSentinel(cursor) && cursor != bytes32(0));
}
/// @notice Check if a set contains a value.
/// @dev This method does not clear the upper bits of `value`, that is expected to be done as part of casting
/// to the correct type. If this function is provided the sentinel value by using the upper bits, this function
/// may returns `true`.
/// @param set The set to check.
/// @param value The value to check for.
/// @return True if the set contains the value, false otherwise.
function contains(LinkedListSet storage set, SetValue value) internal view returns (bool) {
mapping(bytes32 => bytes32) storage map = set.map;
return map[SetValue.unwrap(value)] != bytes32(0);
}
/// @notice Check if a set is empty.
/// @param set The set to check.
/// @return True if the set is empty, false otherwise.
function isEmpty(LinkedListSet storage set) internal view returns (bool) {
mapping(bytes32 => bytes32) storage map = set.map;
bytes32 val = map[SENTINEL_VALUE];
return val == bytes32(0) || isSentinel(val); // either the sentinel is unset, or points to itself
}
/// @notice Get all elements in a set.
/// @dev This is an O(n) operation, where n is the number of elements in the set.
/// @param set The set to get the elements of.
/// @return ret An array of all elements in the set.
function getAll(LinkedListSet storage set) internal view returns (SetValue[] memory ret) {
mapping(bytes32 => bytes32) storage map = set.map;
uint256 size;
bytes32 cursor = map[SENTINEL_VALUE];
// Dynamically allocate the returned array as we iterate through the set, since we don't know the size
// beforehand.
// This is accomplished by first writing to memory after the free memory pointer,
// then updating the free memory pointer to cover the newly-allocated data.
// To the compiler, writes to memory after the free memory pointer are considered "memory safe".
// See https://docs.soliditylang.org/en/v0.8.22/assembly.html#memory-safety
// Stack variable lifting done when compiling with via-ir will only ever place variables into memory
// locations below the current free memory pointer, so it is safe to compile this library with via-ir.
// See https://docs.soliditylang.org/en/v0.8.22/yul.html#memoryguard
assembly ("memory-safe") {
// It is critical that no other memory allocations occur between:
// - loading the value of the free memory pointer into `ret`
// - updating the free memory pointer to point to the newly-allocated data, which is done after all
// the values have been written.
ret := mload(0x40)
}
while (!isSentinel(cursor) && cursor != bytes32(0)) {
unchecked {
++size;
}
// Place the item into the return array manually. Since the size was just incremented, it will point to
// the next location to write to.
assembly ("memory-safe") {
mstore(add(ret, mul(size, 0x20)), cursor)
}
cursor = map[cursor];
}
assembly ("memory-safe") {
// Update the free memory pointer with the now-known length of the array.
mstore(0x40, add(ret, mul(add(size, 1), 0x20)))
// Set the length of the array.
mstore(ret, size)
}
}
function isSentinel(bytes32 value) internal pure returns (bool ret) {
assembly ("memory-safe") {
ret := and(value, 1)
}
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {HookConfig} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {IModule} from "@erc6900/reference-implementation/interfaces/IModule.sol";
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import {toSetValue} from "../account/AccountStorage.sol";
import {ExecutionLib} from "./ExecutionLib.sol";
import {LinkedListSet, LinkedListSetLib} from "./LinkedListSetLib.sol";
/// @title Module Install Commons Library
/// @author Alchemy
/// @notice This is an internal library which holds module installation-related functions relevant to both the
/// ExecutionInstallDelegate and the ModuleManagerInternals contracts.
library ModuleInstallCommonsLib {
using LinkedListSetLib for LinkedListSet;
error InterfaceNotSupported(address module);
error ModuleInstallCallbackFailed(address module, bytes revertReason);
error ExecutionHookAlreadySet(HookConfig hookConfig);
// Internal Functions
// We don't need to bring the exec hook removal function here since it's only ever used in the
// ExecutionInstallLib
/// @dev adds an execution hook to a specific set of hooks.
function addExecHooks(LinkedListSet storage hooks, HookConfig hookConfig) internal {
if (!hooks.tryAdd(toSetValue(hookConfig))) {
revert ExecutionHookAlreadySet(hookConfig);
}
}
/// @dev setup the module storage for the account, reverts are bubbled up into a custom
/// ModuleInstallCallbackFailed
function onInstall(address module, bytes calldata data, bytes4 interfaceId) internal {
if (data.length > 0) {
if (!ERC165Checker.supportsERC165InterfaceUnchecked(module, interfaceId)) {
revert InterfaceNotSupported(module);
}
// solhint-disable-next-line no-empty-blocks
try IModule(module).onInstall(data) {}
catch {
bytes memory revertReason = ExecutionLib.collectReturnData();
revert ModuleInstallCallbackFailed(module, revertReason);
}
}
}
/// @dev clear the module storage for the account, reverts are IGNORED. Status is included in emitted event.
function onUninstall(address module, bytes calldata data) internal returns (bool onUninstallSuccess) {
onUninstallSuccess = true;
if (data.length > 0) {
// Clear the module storage for the account.
// solhint-disable-next-line no-empty-blocks
try IModule(module).onUninstall(data) {}
catch {
onUninstallSuccess = false;
}
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
function getEmptyCalldataSlice() pure returns (bytes calldata) {
bytes calldata empty;
assembly ("memory-safe") {
empty.length := 0
}
return empty;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {ModuleEntity} from "../interfaces/IModularAccount.sol";
// ModuleEntity is a packed representation of a module function
// Layout:
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________________ // Address
// 0x________________________________________BBBBBBBB________________ // Entity ID
// 0x________________________________________________0000000000000000 // unused
library ModuleEntityLib {
function pack(address addr, uint32 entityId) internal pure returns (ModuleEntity) {
return ModuleEntity.wrap(bytes24(bytes20(addr)) | bytes24(uint192(entityId)));
}
function unpack(ModuleEntity moduleEntity) internal pure returns (address addr, uint32 entityId) {
bytes24 underlying = ModuleEntity.unwrap(moduleEntity);
addr = address(bytes20(underlying));
entityId = uint32(bytes4(underlying << 160));
}
function isEmpty(ModuleEntity moduleEntity) internal pure returns (bool) {
return ModuleEntity.unwrap(moduleEntity) == bytes24(0);
}
function notEmpty(ModuleEntity moduleEntity) internal pure returns (bool) {
return ModuleEntity.unwrap(moduleEntity) != bytes24(0);
}
function eq(ModuleEntity a, ModuleEntity b) internal pure returns (bool) {
return ModuleEntity.unwrap(a) == ModuleEntity.unwrap(b);
}
function notEq(ModuleEntity a, ModuleEntity b) internal pure returns (bool) {
return ModuleEntity.unwrap(a) != ModuleEntity.unwrap(b);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {RESERVED_VALIDATION_DATA_INDEX} from "../helpers/Constants.sol";
import {getEmptyCalldataSlice} from "../helpers/EmptyCalldataSlice.sol";
/// @title Sparse Calldata Segment Library
/// @notice Library for working with sparsely-packed calldata segments, identified with an index.
/// @dev The first byte of each segment is the index of the segment.
/// To prevent accidental stack-to-deep errors, the body and index of the segment are extracted separately, rather
/// than inline as part of the tuple returned by `getNextSegment`.
library SparseCalldataSegmentLib {
error NonCanonicalEncoding();
error SegmentOutOfOrder();
error ValidationSignatureSegmentMissing();
/// @notice Splits out a segment of calldata, sparsely-packed.
/// The expected format is:
/// [uint8(index0), uint32(len(segment0)), segment0, uint8(index1), uint32(len(segment1)), segment1,
/// ... uint8(indexN), uint32(len(segmentN)), segmentN]
/// @param source The calldata to extract the segment from.
/// @return segment The extracted segment. Using the above example, this would be segment0.
/// @return remainder The remaining calldata. Using the above example,
/// this would start at uint8(index1) and continue to the end at segmentN.
function getNextSegment(bytes calldata source)
internal
pure
returns (bytes calldata segment, bytes calldata remainder)
{
// The first byte of the segment is the index.
// The next 4 bytes hold the length of the segment, excluding the index.
uint32 length = uint32(bytes4(source[1:5]));
// The offset of the remainder of the calldata.
uint256 remainderOffset = 5 + length;
// The segment is the next `length` bytes after the first 5 bytes.
segment = source[5:remainderOffset];
// The remainder is the rest of the calldata.
remainder = source[remainderOffset:];
}
/// @notice If the index of the next segment in the source equals the provided index, return the next body and
/// advance the source by one segment.
/// @dev Reverts if the index of the next segment is less than the provided index, or if the extracted segment
/// has length 0.
/// @param source The calldata to extract the segment from.
/// @param index The index of the segment to extract.
/// @return A tuple containing the extracted segment's body, or an empty buffer if the index is not found, and
/// the remaining calldata.
function advanceSegmentIfAtIndex(bytes calldata source, uint8 index)
internal
pure
returns (bytes calldata, bytes calldata)
{
uint8 nextIndex = getIndex(source);
if (nextIndex < index) {
revert SegmentOutOfOrder();
}
if (nextIndex == index) {
(bytes calldata segment, bytes calldata remainder) = getNextSegment(source);
if (segment.length == 0) {
revert NonCanonicalEncoding();
}
return (segment, remainder);
}
return (getEmptyCalldataSlice(), source);
}
/// @notice Extracts the final segment from the source.
/// @dev Reverts if the index of the segment is not RESERVED_VALIDATION_DATA_INDEX.
/// @param source The calldata to extract the segment from.
/// @return The final segment.
function getFinalSegment(bytes calldata source) internal pure returns (bytes calldata) {
if (getIndex(source) != RESERVED_VALIDATION_DATA_INDEX) {
revert ValidationSignatureSegmentMissing();
}
return source[1:];
}
/// @notice Extracts the index from a segment.
/// @dev The first byte of the segment is the index.
/// @param segment The segment to extract the index from
/// @return The index of the segment
function getIndex(bytes calldata segment) internal pure returns (uint8) {
return uint8(segment[0]);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {ModuleEntity, ValidationConfig, ValidationFlags} from "../interfaces/IModularAccount.sol";
// Validation config is a packed representation of a validation function and flags for its configuration.
// Layout:
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________________ // Address
// 0x________________________________________BBBBBBBB________________ // Entity ID
// 0x________________________________________________CC______________ // ValidationFlags
// 0x__________________________________________________00000000000000 // unused
// ValidationFlags layout:
// 0b00000___ // unused
// 0b_____A__ // isGlobal
// 0b______B_ // isSignatureValidation
// 0b_______C // isUserOpValidation
library ValidationConfigLib {
// is user op validation flag stored in last bit of the 25th byte
bytes32 internal constant _VALIDATION_FLAG_IS_USER_OP = bytes32(uint256(1) << 56);
// is signature validation flag stored in second to last bit of the 25th byte
bytes32 internal constant _VALIDATION_FLAG_IS_SIGNATURE = bytes32(uint256(1) << 57);
// is global flag stored in the third to last bit of the 25th byte
bytes32 internal constant _VALIDATION_FLAG_IS_GLOBAL = bytes32(uint256(1) << 58);
function pack(
ModuleEntity _validationFunction,
bool _isGlobal,
bool _isSignatureValidation,
bool _isUserOpValidation
) internal pure returns (ValidationConfig) {
return ValidationConfig.wrap(
bytes25(
bytes25(ModuleEntity.unwrap(_validationFunction))
| bytes25(bytes32(_isGlobal ? _VALIDATION_FLAG_IS_GLOBAL : bytes32(0)))
| bytes25(bytes32(_isSignatureValidation ? _VALIDATION_FLAG_IS_SIGNATURE : bytes32(0)))
| bytes25(bytes32(_isUserOpValidation ? _VALIDATION_FLAG_IS_USER_OP : bytes32(0)))
)
);
}
function pack(
address _module,
uint32 _entityId,
bool _isGlobal,
bool _isSignatureValidation,
bool _isUserOpValidation
) internal pure returns (ValidationConfig) {
return ValidationConfig.wrap(
bytes25(
// module address stored in the first 20 bytes
bytes25(bytes20(_module))
// entityId stored in the 21st - 24th byte
| bytes25(bytes24(uint192(_entityId)))
| bytes25(bytes32(_isGlobal ? _VALIDATION_FLAG_IS_GLOBAL : bytes32(0)))
| bytes25(bytes32(_isSignatureValidation ? _VALIDATION_FLAG_IS_SIGNATURE : bytes32(0)))
| bytes25(bytes32(_isUserOpValidation ? _VALIDATION_FLAG_IS_USER_OP : bytes32(0)))
)
);
}
function unpackUnderlying(ValidationConfig config)
internal
pure
returns (address _module, uint32 _entityId, ValidationFlags flags)
{
bytes25 configBytes = ValidationConfig.unwrap(config);
_module = address(bytes20(configBytes));
_entityId = uint32(bytes4(configBytes << 160));
flags = ValidationFlags.wrap(uint8(configBytes[24]));
}
function unpack(ValidationConfig config)
internal
pure
returns (ModuleEntity _validationFunction, ValidationFlags flags)
{
bytes25 configBytes = ValidationConfig.unwrap(config);
_validationFunction = ModuleEntity.wrap(bytes24(configBytes));
flags = ValidationFlags.wrap(uint8(configBytes[24]));
}
function module(ValidationConfig config) internal pure returns (address) {
return address(bytes20(ValidationConfig.unwrap(config)));
}
function entityId(ValidationConfig config) internal pure returns (uint32) {
return uint32(bytes4(ValidationConfig.unwrap(config) << 160));
}
function moduleEntity(ValidationConfig config) internal pure returns (ModuleEntity) {
return ModuleEntity.wrap(bytes24(ValidationConfig.unwrap(config)));
}
function isGlobal(ValidationConfig config) internal pure returns (bool) {
return ValidationConfig.unwrap(config) & _VALIDATION_FLAG_IS_GLOBAL != 0;
}
function isGlobal(ValidationFlags flags) internal pure returns (bool) {
return ValidationFlags.unwrap(flags) & 0x04 != 0;
}
function isSignatureValidation(ValidationConfig config) internal pure returns (bool) {
return ValidationConfig.unwrap(config) & _VALIDATION_FLAG_IS_SIGNATURE != 0;
}
function isSignatureValidation(ValidationFlags flags) internal pure returns (bool) {
return ValidationFlags.unwrap(flags) & 0x02 != 0;
}
function isUserOpValidation(ValidationConfig config) internal pure returns (bool) {
return ValidationConfig.unwrap(config) & _VALIDATION_FLAG_IS_USER_OP != 0;
}
function isUserOpValidation(ValidationFlags flags) internal pure returns (bool) {
return ValidationFlags.unwrap(flags) & 0x01 != 0;
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
import "./PackedUserOperation.sol";
interface IAccountExecute {
/**
* Account may implement this execute method.
* passing this methodSig at the beginning of callData will cause the entryPoint to pass the full UserOp (and hash)
* to the account.
* The account should skip the methodSig, and use the callData (and optionally, other UserOp fields)
*
* @param userOp - The operation that was just validated.
* @param userOpHash - Hash of the user's request data.
*/
function executeUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC1155Receiver} from "../token/ERC1155/IERC1155Receiver.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC1271 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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* 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[EIP 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.4;
/// @notice UUPS proxy mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol)
/// @author Modified from OpenZeppelin
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol)
///
/// @dev Note:
/// - This implementation is intended to be used with ERC1967 proxies.
/// See: `LibClone.deployERC1967` and related functions.
/// - This implementation is NOT compatible with legacy OpenZeppelin proxies
/// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`.
abstract contract UUPSUpgradeable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The upgrade failed.
error UpgradeFailed();
/// @dev The call is from an unauthorized call context.
error UnauthorizedCallContext();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For checking if the context is a delegate call.
uint256 private immutable __self = uint256(uint160(address(this)));
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when the proxy's implementation is upgraded.
event Upgraded(address indexed implementation);
/// @dev `keccak256(bytes("Upgraded(address)"))`.
uint256 private constant _UPGRADED_EVENT_SIGNATURE =
0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ERC-1967 storage slot for the implementation in the proxy.
/// `uint256(keccak256("eip1967.proxy.implementation")) - 1`.
bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UUPS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to check if `msg.sender` is authorized
/// to upgrade the proxy to `newImplementation`, reverting if not.
/// ```
/// function _authorizeUpgrade(address) internal override onlyOwner {}
/// ```
function _authorizeUpgrade(address newImplementation) internal virtual;
/// @dev Returns the storage slot used by the implementation,
/// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822).
///
/// Note: The `notDelegated` modifier prevents accidental upgrades to
/// an implementation that is a proxy contract.
function proxiableUUID() public view virtual notDelegated returns (bytes32) {
// This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967.
return _ERC1967_IMPLEMENTATION_SLOT;
}
/// @dev Upgrades the proxy's implementation to `newImplementation`.
/// Emits a {Upgraded} event.
///
/// Note: Passing in empty `data` skips the delegatecall to `newImplementation`.
function upgradeToAndCall(address newImplementation, bytes calldata data)
public
payable
virtual
onlyProxy
{
_authorizeUpgrade(newImplementation);
/// @solidity memory-safe-assembly
assembly {
newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits.
mstore(0x01, 0x52d1902d) // `proxiableUUID()`.
let s := _ERC1967_IMPLEMENTATION_SLOT
// Check if `newImplementation` implements `proxiableUUID` correctly.
if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) {
mstore(0x01, 0x55299b49) // `UpgradeFailed()`.
revert(0x1d, 0x04)
}
// Emit the {Upgraded} event.
log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation)
sstore(s, newImplementation) // Updates the implementation.
// Perform a delegatecall to `newImplementation` if `data` is non-empty.
if data.length {
// Forwards the `data` to `newImplementation` via delegatecall.
let m := mload(0x40)
calldatacopy(m, data.offset, data.length)
if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00))
{
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
}
}
/// @dev Requires that the execution is performed through a proxy.
modifier onlyProxy() {
uint256 s = __self;
/// @solidity memory-safe-assembly
assembly {
// To enable use cases with an immutable default implementation in the bytecode,
// (see: ERC6551Proxy), we don't require that the proxy address must match the
// value stored in the implementation slot, which may not be initialized.
if eq(s, address()) {
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
revert(0x1c, 0x04)
}
}
_;
}
/// @dev Requires that the execution is NOT performed via delegatecall.
/// This is the opposite of `onlyProxy`.
modifier notDelegated() {
uint256 s = __self;
/// @solidity memory-safe-assembly
assembly {
if iszero(eq(s, address())) {
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
revert(0x1c, 0x04)
}
}
_;
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
// solhint-disable-next-line private-vars-leading-underscore
function _coalescePreValidation(uint256 validationRes1, uint256 validationRes2)
pure
returns (uint256 resValidationData)
{
resValidationData = _coalesceValidationResTime(validationRes1, validationRes2);
// Once we know that the authorizer field is 0 or 1, we can safely bubble up SIG_FAIL with bitwise OR
resValidationData |= uint160(validationRes1) | uint160(validationRes2);
}
// solhint-disable-next-line private-vars-leading-underscore
function _coalesceValidation(uint256 preValidationData, uint256 validationRes)
pure
returns (uint256 resValidationData)
{
resValidationData = _coalesceValidationResTime(preValidationData, validationRes);
// If prevalidation failed, bubble up failure, and ignore authorizer
resValidationData |= uint160(preValidationData) == 1 ? 1 : uint160(validationRes);
}
// solhint-disable-next-line private-vars-leading-underscore
function _coalesceValidationResTime(uint256 validationRes1, uint256 validationRes2)
pure
returns (uint256 resValidationData)
{
uint48 validUntil1 = uint48(validationRes1 >> 160);
if (validUntil1 == 0) {
validUntil1 = type(uint48).max;
}
uint48 validUntil2 = uint48(validationRes2 >> 160);
if (validUntil2 == 0) {
validUntil2 = type(uint48).max;
}
resValidationData = ((validUntil1 > validUntil2) ? uint256(validUntil2) << 160 : uint256(validUntil1) << 160);
uint48 validAfter1 = uint48(validationRes1 >> 208);
uint48 validAfter2 = uint48(validationRes2 >> 208);
resValidationData |= ((validAfter1 < validAfter2) ? uint256(validAfter2) << 208 : uint256(validAfter1) << 208);
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
interface IModularAccountBase {
/// @notice Create a contract.
/// @param value The value to send to the new contract constructor
/// @param initCode The initCode to deploy.
/// @param isCreate2 The bool to indicate which method to use to deploy.
/// @param salt The salt for deployment.
/// @return createdAddr The created contract address.
function performCreate(uint256 value, bytes calldata initCode, bool isCreate2, bytes32 salt)
external
payable
returns (address createdAddr);
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {IExecutionHookModule} from "@erc6900/reference-implementation/interfaces/IExecutionHookModule.sol";
import {HookConfig} from "@erc6900/reference-implementation/interfaces/IModularAccountView.sol";
import {IValidationHookModule} from "@erc6900/reference-implementation/interfaces/IValidationHookModule.sol";
import {IValidationModule} from "@erc6900/reference-implementation/interfaces/IValidationModule.sol";
import {HookConfigLib} from "@erc6900/reference-implementation/libraries/HookConfigLib.sol";
import {ModuleEntity} from "@erc6900/reference-implementation/libraries/ModuleEntityLib.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
// The following types alias a `bytes memory`, to protect the user from doing anything unexpected with it. We can't
// actually alias a `bytes memory` type, so we use a `bytes32` type instead, and cast it to `bytes memory` within
// this library.
type UOCallBuffer is bytes32;
type RTCallBuffer is bytes32;
type PHCallBuffer is bytes32;
type SigCallBuffer is bytes32;
type DensePostHookData is bytes32;
using HookConfigLib for HookConfig;
/// @title Execution Library
/// @author Alchemy
/// @notice A library for performing external calls. This library is used for the external calls of `execute` and
/// `executeBatch`, for any account self-calls, and for any call to a module function.
/// @dev This library uses "call buffers", or reusable memory buffers that hold the abi-encoded data to be sent to
/// a module function. These buffers are used to avoid the overhead of encoding the same data multiple times.
library ExecutionLib {
// solhint-disable ordering
// Functions are more readable in original order.
error PostExecHookReverted(ModuleEntity moduleFunction, bytes revertReason);
error PreExecHookReverted(ModuleEntity moduleFunction, bytes revertReason);
error PreRuntimeValidationHookReverted(ModuleEntity moduleFunction, bytes revertReason);
error PreSignatureValidationHookReverted(ModuleEntity moduleFunction, bytes revertReason);
error PreUserOpValidationHookReverted(ModuleEntity moduleFunction, bytes revertReason);
error RuntimeValidationFunctionReverted(ModuleEntity moduleFunction, bytes revertReason);
error SignatureValidationFunctionReverted(ModuleEntity moduleFunction, bytes revertReason);
error UserOpValidationFunctionReverted(ModuleEntity moduleFunction, bytes revertReason);
/// @notice Perform the following call, without capturing any return data.
/// If the call reverts, the revert message will be directly bubbled up.
/// @param target The address to call.
/// @param value The value to send with the call.
/// @param callData The data to send with the call.
function callBubbleOnRevert(address target, uint256 value, bytes memory callData) internal {
// Manually call, without collecting return data unless there's a revert.
assembly ("memory-safe") {
let success :=
call(
gas(),
target,
/*value*/
value,
/*argOffset*/
add(callData, 0x20),
/*argSize*/
mload(callData),
/*retOffset*/
codesize(),
/*retSize*/
0
)
// directly bubble up revert messages, if any.
if iszero(success) {
// For memory safety, copy this revert data to scratch space past the end of used memory. Because
// we immediately revert, we can omit storing the length as we normally would for a `bytes memory`
// type, as well as omit finalizing the allocation by updating the free memory pointer.
let revertDataLocation := mload(0x40)
returndatacopy(revertDataLocation, 0, returndatasize())
revert(revertDataLocation, returndatasize())
}
}
}
/// @notice Transiently copy the call data to a memory, and perform a self-call.
/// If the call reverts, the revert message will be directly bubbled up.
/// @param target The address to call.
/// @param value The value to send with the call.
/// @param callData The data to send with the call.
function callBubbleOnRevertTransient(address target, uint256 value, bytes calldata callData) internal {
bytes memory encodedCall;
assembly ("memory-safe") {
// Store the length of the call
encodedCall := mload(0x40)
mstore(encodedCall, callData.length)
// Copy in the calldata
calldatacopy(add(encodedCall, 0x20), callData.offset, callData.length)
}
callBubbleOnRevert(target, value, encodedCall);
// Memory is discarded afterwards
}
// Transiently copy the call data to a memory, and perform a self-call.
function delegatecallBubbleOnRevertTransient(address target) internal {
assembly ("memory-safe") {
// Store the length of the call
let fmp := mload(0x40)
// Copy in the entire calldata
calldatacopy(fmp, 0, calldatasize())
let success :=
delegatecall(
gas(),
target,
/*argOffset*/
fmp,
/*argSize*/
calldatasize(),
/*retOffset*/
codesize(),
/*retSize*/
0
)
// directly bubble up revert messages, if any.
if iszero(success) {
// For memory safety, copy this revert data to scratch space past the end of used memory. Because
// we immediately revert, we can omit storing the length as we normally would for a `bytes memory`
// type, as well as omit finalizing the allocation by updating the free memory pointer.
let revertDataLocation := mload(0x40)
returndatacopy(revertDataLocation, 0, returndatasize())
revert(revertDataLocation, returndatasize())
}
}
// Memory is discarded afterwards
}
/// @notice Manually collect and store the return data from the most recent external call into a `bytes
/// memory`.
/// @return returnData The return data from the most recent external call.
function collectReturnData() internal pure returns (bytes memory returnData) {
assembly ("memory-safe") {
// Allocate a buffer of that size, advancing the memory pointer to the nearest word
returnData := mload(0x40)
mstore(returnData, returndatasize())
mstore(0x40, and(add(add(returnData, returndatasize()), 0x3f), not(0x1f)))
// Copy over the return data
returndatacopy(add(returnData, 0x20), 0, returndatasize())
}
}
// Allocate a buffer to call user op validation and validation hook functions. Both of these take the form of
// - bytes4 selector
// - uint32 entityId
// - PackedUserOperation userOp
// - bytes32 userOpHash
// The buffer starts with the selector for `preUserOpValidationHook`, and can be updated later to
// `validateUserOp`. When perfomring the actual function calls later, update the entityId field and selector,
// as as needed.
function allocateUserOpValidationCallBuffer(PackedUserOperation calldata userOp, bytes32 userOpHash)
internal
pure
returns (UOCallBuffer result)
{
bytes memory buffer =
abi.encodeCall(IValidationHookModule.preUserOpValidationHook, (uint32(0), userOp, userOpHash));
assembly ("memory-safe") {
result := buffer
}
// Buffer contents:
// 0xAAAAAAAA // selector
// 0x000: 0x________________________________________________________BBBBBBBB // entityId
// 0x020: 0x______________________________________________________________60 // userOp offset
// 0x040: 0xCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC // userOp hash
// 0x060: 0x________________________DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD // userOp sender
// 0x080: 0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE // userOp nonce
// 0x0a0: 0x_____________________________________________________________FFF // userOp initCode offset
// 0x0c0: 0x_____________________________________________________________GGG // userOp callData offset
// 0x0e0: 0xHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH // userOp accountGasLimits
// 0x100: 0xIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII // userOp preVerificationGas
// 0x120: 0xJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ // userOp gasFees
// 0x140: 0x_____________________________________________________________KKK // userOp pmData offset
// 0x160: 0x_____________________________________________________________LLL // userOp signature offset
// 0x180... // dynamic fields
}
// Converts a user op call buffer from pre user op validation hooks to user op validation.
// Performs this by writing over the selector stored in the buffer.
function convertToValidationBuffer(UOCallBuffer buffer) internal pure {
// Selector is treated as a uint32 to be right-aligned in the word.
uint32 selector = uint32(IValidationModule.validateUserOp.selector);
assembly ("memory-safe") {
// We want to write in the selector without writing over anything else in the buffer, so we save the
// length, write over a portion of the length, and restore it.
let bufferLength := mload(buffer)
mstore(add(buffer, 4), selector)
mstore(buffer, bufferLength)
}
}
// Invokes either a user op validation hook, or validation function.
function invokeUserOpCallBuffer(
UOCallBuffer buffer,
ModuleEntity moduleEntity,
bytes calldata signatureSegment
) internal returns (uint256 validationData) {
bool success;
address moduleAddress;
uint32 entityId;
assembly ("memory-safe") {
// Load the module address and entity Id
entityId := and(shr(64, moduleEntity), 0xffffffff)
moduleAddress := shr(96, moduleEntity)
// Update the buffer with the entity Id
mstore(add(buffer, 0x24), entityId)
// Get the offset of the user op signature in the buffer.
// The PackedUserOperation starts at the 5th word in the buffer (0x20 * 4 = 0x80).
// It is the 9th element in PackedUserOp (so add 0x20 * 8 = 0x100 to the buffer start).
// So we start at 0x184, to include the selector length.
// Then, to convert from a relative to an absolute offset, we need to add the buffer start, selector,
// and to skip over the entityId, offset, and hash.
let userOpSigRelativeOffset := mload(add(buffer, 0x184))
let userOpSigAbsOffset := add(add(buffer, userOpSigRelativeOffset), 0x84)
// Copy in the signature segment
// Since the buffer's copy of the signature exceeds the length of any sub-segments, we can safely write
// over it.
mstore(userOpSigAbsOffset, signatureSegment.length)
// If there is a nonzero signature segment length, copy in the data.
if signatureSegment.length {
// Because we will be sending the data with word-aligned padding ("strict ABI encoding"), we need
// to zero out the last word of the buffer to prevent sending garbage data.
let roundedDownSignatureLength := and(signatureSegment.length, not(0x1f))
mstore(add(userOpSigAbsOffset, add(roundedDownSignatureLength, 0x20)), 0)
calldatacopy(add(userOpSigAbsOffset, 0x20), signatureSegment.offset, signatureSegment.length)
}
// The data amount we actually want to call with is:
// buffer length - word-align(oldSignature length) + word-align(newSignature length)
// Which is equivalent to:
// 4 (selector length) + 0x80 (entityId, user op offset, user op hash, signature length field)
// + userOpSigRelativeOffset + word-align(newSignature length)
let actualCallLength := add(userOpSigRelativeOffset, 0x84)
// Add in the new signature length, with word alignment. This is safe to do because the signature
// segment length is guaranteed to be less than the size of the previous entire signature length.
actualCallLength := add(actualCallLength, and(add(signatureSegment.length, 0x1f), not(0x1f)))
// Perform the call, reverting on failure or insufficient return data.
success :=
and(
// Yul evaluates expressions from right to left, so `returndatasize` will evaluate after `call`.
gt(returndatasize(), 0x1f),
call(
// If gas is the leftmost item before the call, it *should* be placed immediately before the
// call opcode and be allowed in validation.
gas(),
moduleAddress,
/*value*/
0,
/*argOffset*/
add(buffer, 0x20), // jump over 32 bytes for length
/*argSize*/
actualCallLength,
/*retOffset*/
0,
/*retSize*/
0x20
)
)
}
if (success) {
assembly ("memory-safe") {
// If the call was successful, we return the first word of the return data as the validation data.
validationData := mload(0)
}
} else {
// Revert with the appropriate error type for the selector used.
uint32 selectorUsed;
uint32 errorSelector;
assembly ("memory-safe") {
selectorUsed := and(mload(add(buffer, 0x4)), 0xffffffff)
}
if (selectorUsed == uint32(IValidationHookModule.preUserOpValidationHook.selector)) {
errorSelector = uint32(PreUserOpValidationHookReverted.selector);
} else {
errorSelector = uint32(UserOpValidationFunctionReverted.selector);
}
_revertModuleFunction(errorSelector, moduleAddress, entityId);
}
}
function allocateRuntimeValidationCallBuffer(bytes calldata callData, bytes calldata authorization)
internal
returns (RTCallBuffer result)
{
// Allocate a call to regular runtime validation. Pre runtime validation hooks lack the `account` field, so
// they won't touch the selector portion of this buffer.
bytes memory buffer = abi.encodeCall(
IValidationModule.validateRuntime,
(address(0), uint32(0), msg.sender, msg.value, callData, authorization)
);
assembly ("memory-safe") {
result := buffer
}
// Buffer contents, before update:
// 0xAAAAAAAA // selector
// 0x000: 0x________________________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB // account
// 0x020: 0x________________________________________________________CCCCCCCC // entityId
// 0x040: 0x________________________DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD // msg.sender
// 0x060: 0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE // msg.value
// 0x080: 0x______________________________________________________________c0 // callData offset
// 0x0a0: 0x_____________________________________________________________FFF // authorization offset
// 0x0c0... // dynamic fields
// Prepare the buffer for pre-runtime validation hooks.
_prepareRuntimeCallBufferPreValidationHooks(result);
}
function invokeRuntimeCallBufferPreValidationHook(
RTCallBuffer buffer,
HookConfig hookEntity,
bytes calldata authorizationSegment
) internal {
bool success;
address moduleAddress;
uint32 entityId;
assembly ("memory-safe") {
// Load the module address and entity Id
entityId := and(shr(64, hookEntity), 0xffffffff)
moduleAddress := shr(96, hookEntity)
// Update the buffer with the entity Id
mstore(add(buffer, 0x44), entityId)
// Get the offset of the authorization in the buffer.
// The authorization offset is the 6th word in the buffer (0x20 * 5 = 0xa0).
// We need to add the buffer length and selector length (0x24) to get the start of the authorization.
let authorizationRelativeOffset := mload(add(buffer, 0xc4))
// Convert to an absolute offset
// Add the lengths of the selector, buffer length field, and the authorization length field.
let authorizationAbsOffset := add(add(buffer, authorizationRelativeOffset), 0x44)
// Copy in the authorization segment
// Since the buffer's copy of the authorization exceeds the length of any sub-segments, we can safely
// write over it.
mstore(authorizationAbsOffset, authorizationSegment.length)
// If there is a nonzero authorization segment length, copy in the data.
if authorizationSegment.length {
// Because we will be sending the data with word-aligned padding ("strict ABI encoding"), we need
// to zero out the last word of the buffer to prevent sending garbage data.
let roundedDownAuthorizationLength := and(authorizationSegment.length, not(0x1f))
mstore(add(authorizationAbsOffset, add(roundedDownAuthorizationLength, 0x20)), 0)
// Copy the authorization segment from calldata into the correct location in the buffer.
calldatacopy(
add(authorizationAbsOffset, 0x20), authorizationSegment.offset, authorizationSegment.length
)
}
// The data amount we actually want to call with is:
// buffer length - word-align(oldAuthorization length) + word-align(newAuthorization length) - 0x20 (to
// skip `account`),
// This is equivalent to:
// 4 (selector length) + 0x20 (authorization length field) + authorizationRelativeOffset +
// word-align(newAuthorization length)
let actualCallLength := add(authorizationRelativeOffset, 0x24)
// Add in the new authorization length, with word alignment. This is safe to do because the
// authorization segment length is guaranteed to be less than the size of the previous entire
// authorization length.
actualCallLength := add(actualCallLength, and(add(authorizationSegment.length, 0x1f), not(0x1f)))
// Perform the call
success :=
call(
gas(),
moduleAddress,
/*value*/
0,
/*argOffset*/
add(buffer, 0x40), // jump over 32 bytes for length, and another 32 bytes for the account
/*argSize*/
actualCallLength,
/*retOffset*/
codesize(),
/*retSize*/
0
)
}
if (!success) {
_revertModuleFunction(uint32(PreRuntimeValidationHookReverted.selector), moduleAddress, entityId);
}
}
// Note: we need to add an extra check for codesize > 0 on the module, otherwise EOAs added as runtime
// validation would authorize all calls.
function invokeRuntimeCallBufferValidation(
RTCallBuffer buffer,
ModuleEntity moduleEntity,
bytes calldata authorizationSegment
) internal {
bool success;
address moduleAddress;
uint32 entityId;
assembly ("memory-safe") {
// Load the module address and entity Id
entityId := and(shr(64, moduleEntity), 0xffffffff)
moduleAddress := shr(96, moduleEntity)
// Store the account in the `account` field.
mstore(add(buffer, 0x24), address())
// Update the buffer with the entity Id
mstore(add(buffer, 0x44), entityId)
// Fix the calldata offsets of `callData` and `authorization`, due to including the `account` field for
// runtime validation.
// The offset of calldata should be reset back to 0x0c0. For pre-validation hooks, it was set to 0x0a0.
mstore(add(buffer, 0xa4), 0xc0)
// Get the offset of the authorization in the buffer.
// The authorization offset is the 6th word in the buffer (0x20 * 5 = 0xa0).
let authorizationOffsetPtr := add(buffer, 0xc4)
// Get the stored value. This will be the edited value for preRuntimeValidationHooks.
let authorizationRelativeOffset := mload(authorizationOffsetPtr)
// Fix the stored offset value by adding 0x20.
authorizationRelativeOffset := add(authorizationRelativeOffset, 0x20)
// Correct the authorization relative offset
mstore(authorizationOffsetPtr, authorizationRelativeOffset)
// Convert to an absolute offset
// Add the lengths of the selector and buffer length field.
let authorizationAbsOffset := add(add(buffer, authorizationRelativeOffset), 0x24)
// Copy in the authorization segment
// Since the buffer's copy of the authorization exceeds the length of any sub-segments, we can safely
// write over it.
mstore(authorizationAbsOffset, authorizationSegment.length)
// If there is a nonzero authorization segment length, copy in the data.
if authorizationSegment.length {
// Because we will be sending the data with word-aligned padding ("strict ABI encoding"), we need
// to zero out the last word of the buffer to prevent sending garbage data.
let roundedDownAuthorizationLength := and(authorizationSegment.length, not(0x1f))
mstore(add(authorizationAbsOffset, add(roundedDownAuthorizationLength, 0x20)), 0)
// Copy the authorization segment from calldata into the correct location in the buffer.
calldatacopy(
add(authorizationAbsOffset, 0x20), authorizationSegment.offset, authorizationSegment.length
)
}
// The data amount we actually want to call with is:
// buffer length - word-align(oldAuthorization length) + word-align(newAuthorization length) - 0x20 (to
// skip `account`),
// This is equivalent to:
// 4 (selector length) + 0x20 (authorization length field) + authorizationRelativeOffset +
// word-align(newAuthorization length)
let actualCallLength := add(authorizationRelativeOffset, 0x24)
// Add in the new authorization length, with word alignment.
// This is safe to do because the authorization segment length is guaranteed to be less than the size
// of the previous entire authorization length.
actualCallLength := add(actualCallLength, and(add(authorizationSegment.length, 0x1f), not(0x1f)))
// Before performing the call, we need to check that the module has code.
// IValidationModule.validateRuntime has no return value, so an EOA added as a validation (perhaps for
// direct call validation) would authorize all calls, which is unsafe. Solidity inserts this check by
// default, but when we're making calls manually via call buffers, we need to do the check ourselves.
if iszero(extcodesize(moduleAddress)) { revert(0, 0) }
// Perform the call
success :=
call(
gas(),
moduleAddress,
/*value*/
0,
/*argOffset*/
add(buffer, 0x20), // jump over 32 bytes for length
/*argSize*/
actualCallLength,
/*retOffset*/
codesize(),
/*retSize*/
0
)
}
if (!success) {
_revertModuleFunction(uint32(RuntimeValidationFunctionReverted.selector), moduleAddress, entityId);
}
}
function executeRuntimeSelfCall(RTCallBuffer buffer, bytes calldata data) internal {
bool bufferExists;
assembly ("memory-safe") {
bufferExists := iszero(iszero(buffer))
}
if (bufferExists) {
// We don’t know whether the RTCallBuffer was called only with pre-hooks (skipping RT validation
// updates because of SMA), or if it was called with a buffer after a module based RT validation, which
// would cause the relative offset of calldata to change. So, when loading the callData for self-exec,
// we must not load the relative calldata offset, and must instead use an absolute offset from the
// start of the buffer. Using an absolute offset is safe because the abi encoder will only generate
// “strict encoding mode” encodings, so it is guaranteed to be in that location.
bytes memory callData;
assembly ("memory-safe") {
// Get the memory address of the length of callData in the buffer.
// Because we don't know whether this will be invoked with the RTCallBuffer still as a
callData := add(buffer, 0xe4)
}
// Perform the call, bubbling up revert data on failure.
callBubbleOnRevert(address(this), msg.value, callData);
} else {
// No buffer exists yet, just copy the data to memory transiently and execute it.
callBubbleOnRevertTransient(address(this), msg.value, data);
}
}
// Convert a RTCallBuffer to a pre hook call buffer, if the RTCallBuffer exists. If not, allocate a new one.
function convertToPreHookCallBuffer(RTCallBuffer buffer, bytes calldata data)
internal
view
returns (PHCallBuffer result)
{
bool bufferExists;
assembly ("memory-safe") {
bufferExists := iszero(iszero(buffer))
}
if (bufferExists) {
// The buffer already has most of what we need, but we need to update the pointer, length, and data
// offset.
// Buffer transformation:
// 0xAAAAAAAA // selector
// 0x000: 0x________________________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB account -> discarded
// 0x020: 0x________________________________________________________CCCCCCCC entityId -> selector
// 0x040: 0x________________________DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD msg.sender -> entityId
// 0x060: 0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE msg.value -> sender
// 0x080: 0x______________________________________________________________c0 callData offset -> value
// 0x0a0: 0x_____________________________________________________________FFF auth offset -> cd offset
// 0x0c0: 0x_____________________________________________________________GGG callData length -> stays
// This new buffer will be a subset of the existing buffer.
PHCallBuffer newBuffer;
// Right-align the selector
uint32 selector = uint32(IExecutionHookModule.preExecutionHook.selector);
assembly ("memory-safe") {
// We don’t know whether the RTCallBuffer was called only with pre-hooks (skipping RT validation
// updates because of SMA), or if it was called with a buffer after a module based RT validation,
// which would cause the relative offset of calldata to change. So, when converting to a pre hook
// buffer, we must not load the relative calldata offset, and must instead use an absolute offset
// from the start of the buffer. Using an absolute offset is safe because the abi encoder will only
// generate “strict encoding mode” encodings, so it is guaranteed to be in that location.
let callDataAbsOffset := add(buffer, 0xe4)
let callDataSize := mload(callDataAbsOffset)
// We must squash existing elements, because the stored offset of authorization causes the other
// fields to not be aligned.
// We need to copy in the selector, entityId, sender, value, and relative callData offset.
// Step back 5 words, to start pasting in the new data.
let workingPtr := add(buffer, 0x44)
// Paste in the selector
mstore(workingPtr, selector)
// skip pasting in the entity ID, the caller will squash this later
workingPtr := add(workingPtr, 0x40)
// Paste in msg.sender
mstore(workingPtr, caller())
workingPtr := add(workingPtr, 0x20)
// Paste in msg.value
mstore(workingPtr, callvalue())
workingPtr := add(workingPtr, 0x20)
// Paste in the relative callData offset. This is now 0xa0, to show that it is after the entityId,
// sender, value, and offset fields.
mstore(workingPtr, 0x80)
// Now store the buffer length. This will be directly before the selector, and the returned pointer
// will point to this word in memory.
newBuffer := add(buffer, 0x40)
// word-align the callDataSize
callDataSize := and(add(callDataSize, 0x1f), not(0x1f))
mstore(newBuffer, add(callDataSize, 0xa4))
// See `allocateRuntimeCallBuffer` for the buffer layout.
}
return newBuffer;
} else {
// We need to allocate and return a new buffer.
return allocatePreExecHookCallBuffer(data);
}
}
// Allocate a buffer to call a pre-execution hook.
function allocatePreExecHookCallBuffer(bytes calldata data) internal view returns (PHCallBuffer) {
bytes memory newBuffer =
abi.encodeCall(IExecutionHookModule.preExecutionHook, (uint32(0), msg.sender, msg.value, data));
PHCallBuffer result;
assembly ("memory-safe") {
result := newBuffer
}
return result;
// Buffer contents:
// 0xAAAAAAAA // selector
// 0x000: 0x________________________________________________________BBBBBBBB // entityId
// 0x020: 0x________________________CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC // sender
// 0x040: 0xDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD // value
// 0x060: 0x______________________________________________________________80 // callData offset
// 0x080... // dynamic fields
}
function invokePreExecHook(PHCallBuffer buffer, HookConfig hookEntity)
internal
returns (uint256 returnedBytesSize)
{
bool success;
address moduleAddress;
uint32 entityId;
assembly ("memory-safe") {
// Load the module address and entity Id
entityId := and(shr(64, hookEntity), 0xffffffff)
moduleAddress := shr(96, hookEntity)
// Update the buffer with the entity Id
mstore(add(buffer, 0x24), entityId)
// Perform the call, storing the first two words of return data into scratch space.
success :=
call(
gas(),
moduleAddress,
/*value*/
0,
/*argOffset*/
add(buffer, 0x20), // jump over 32 bytes for length
/*argSize*/
mload(buffer),
/*retOffset*/
0,
/*retSize*/
0x40
)
// Need at least 64 bytes of return data to be considered successful.
success := and(success, gt(returndatasize(), 0x3f))
// Only accept return data of "strict encoding" form, where the relative offset is exactly 0x20.
success := and(success, eq(mload(0), 0x20))
// Ensure that the reported length of return data does not exceed the actual length.
// aka the stored length <= retundatasize() - 0x40 (for the first two values)
// No opcode for lte, so the expression equals:
// stored length < retundatasize() - 0x3f
// Underflow doesn't matter, because success is false anyways if length < 0x40.
returnedBytesSize := mload(0x20)
success := and(success, lt(returnedBytesSize, sub(returndatasize(), 0x3f)))
}
if (!success) {
_revertModuleFunction(uint32(PreExecHookReverted.selector), moduleAddress, entityId);
}
}
// Converts a PreHookCallBuffer to a `bytes memory`, to use for a self-call in `executeUserOp`.
// Handles skipping ahead an extra 4 bytes to omit the `executeUserOp` selector, and updates the stored length
// to do so. This will edit the buffer.
function getExecuteUOCallData(PHCallBuffer buffer, bytes calldata callData)
internal
pure
returns (bytes memory)
{
bool bufferExists;
assembly ("memory-safe") {
bufferExists := iszero(iszero(buffer))
}
if (bufferExists) {
// At this point, the buffer contains the encoded call to the pre-exec hook, but the data being sent is
// `msg.data`, not `userOp.callData`. Re-decoding the user op struct's callData is error-prone, so
// instead we just copy-in the provided userOp.callData, squashing the buffer. This is fine because the
// buffer will not be reused after this operation.
bytes memory result;
assembly ("memory-safe") {
// Safe to do unchecked because there must have been at least 4 bytes of callData for the
// EntryPoint to call `executeUserOp`.
let actualCallDataLength := sub(callData.length, 4)
// Write over the existing buffer
result := buffer
// Store the new length
mstore(result, actualCallDataLength)
if actualCallDataLength {
// We don't need to write a zero word because this data will not be word-aligned before sending
// Copy in the callData
calldatacopy(add(result, 0x20), add(callData.offset, 4), actualCallDataLength)
}
}
return result;
} else {
// No buffer exists yet, just copy the data to memory and return it.
// Skip the first 4 bytes in this function to save the computation on the buffer reuse case.
return callData[4:];
}
}
// DensePostHookData layout
// Very tricky to navigate, because we must do so backwards.
// type ~= struct[] but in reverse, the caller must advance through it backwards
// N instances of:
// - post hook address (will be squashed with the selector later, during invocation)
// - post hook entity Id
// - fixed preExecHookData offset (always 0x40)
// - preExecHookData length
// - var-length data (right-padded with zeros to be word aligned)
// - segment (struct) length (not counting this word, to traverse backwards)
// 1 count of post hooks to run. The returned memory pointer will point to this value.
function doPreHooks(HookConfig[] memory hooks, PHCallBuffer callBuffer)
internal
returns (DensePostHookData result)
{
uint256 hooksLength = hooks.length;
// How many "post hooks to run" there are.
uint256 resultCount;
// Where in memory to start writing the next "post hook to run".
bytes32 workingMemPtr;
// Start allocating the dense buffer. From this point out, avoid any high-level memory allocations,
// otherwise the data-in-flight may be corrupted.
assembly ("memory-safe") {
workingMemPtr := mload(0x40)
}
// Run the pre hooks and copy their return data to the dense post hooks data buffer array, if an associated
// post exec hook exists.
for (uint256 i = hooksLength; i > 0;) {
// Decrement here, instead of in the loop update step, to handle the case where the length is 0.
unchecked {
--i;
}
HookConfig hookConfig = hooks[i];
if (hookConfig.hasPreHook()) {
uint256 returnedBytesSize = invokePreExecHook(callBuffer, hookConfig);
// If there is an associated post exec hook, save the return data.
if (hookConfig.hasPostHook()) {
// Case: both pre and post exec hook, need to save hook info, and pre hook return data
workingMemPtr = _appendPostHookToRun(workingMemPtr, hookConfig, returnedBytesSize);
++resultCount;
}
} else if (hookConfig.hasPostHook()) {
// If there is no pre hook, but there is a post hook, we still need to save a placeholder for the
// post hook return data.
// Case: only post exec hook, need to save hook info, and no pre hook return data
// Call the append function with legnth 0 to put no pre hook return data.
workingMemPtr = _appendPostHookToRun(workingMemPtr, hookConfig, 0);
++resultCount;
}
}
// Save the length, return a pointer to the length, and update the FMP
assembly ("memory-safe") {
mstore(workingMemPtr, resultCount)
result := workingMemPtr
workingMemPtr := add(workingMemPtr, 0x20)
mstore(0x40, workingMemPtr)
}
}
function doCachedPostHooks(DensePostHookData postHookData) internal {
uint256 postHookCount;
uint256 workingMemPtr;
assembly ("memory-safe") {
postHookCount := mload(postHookData)
workingMemPtr := sub(postHookData, 0x20)
}
uint32 selector = uint32(IExecutionHookModule.postExecutionHook.selector);
// Run the post hooks.
// This is tricky, unlike normal, we must traverse the data backwards, because the post exec hooks should
// be executed in reverse order of the pre exec hooks.
for (uint256 i = 0; i < postHookCount; ++i) {
bool success;
address moduleAddress;
uint32 entityId;
assembly ("memory-safe") {
// The last word of each segment is the segment length
let segmentLength := mload(workingMemPtr)
// Step the working memory pointer back to the start of the segment, and preserve a copy to
// continue the loop
workingMemPtr := sub(workingMemPtr, segmentLength)
let segmentStart := workingMemPtr
// Load the post hook address
moduleAddress := mload(workingMemPtr)
// Load the entity id, just for the revert message
entityId := mload(add(workingMemPtr, 0x20))
// Squash the post hook address field with the selector
mstore(workingMemPtr, selector)
// Advance the working mem pointer to just before the selector, to prepare to make the call.
workingMemPtr := add(workingMemPtr, 0x1c)
// Compute the total call length, including the selector
// This will be seggment length - 0x1c (28), to take out the space not used in the selector
let callLength := sub(segmentLength, 0x1c)
// Perform the call
success :=
call(
gas(),
moduleAddress,
/*value*/
0,
/*argOffset*/
workingMemPtr,
/*argSize*/
callLength,
/*retOffset*/
codesize(),
/*retSize*/
0
)
// Step the working mem pointer back to the previous segment
workingMemPtr := sub(segmentStart, 0x20)
}
if (!success) {
_revertModuleFunction(uint32(PostExecHookReverted.selector), moduleAddress, entityId);
}
}
}
function allocateSigCallBuffer(bytes32 hash, bytes calldata signature)
internal
view
returns (SigCallBuffer result)
{
bytes memory buffer = abi.encodeCall(
IValidationModule.validateSignature, (address(0), uint32(0), msg.sender, hash, signature)
);
assembly ("memory-safe") {
result := buffer
}
// Buffer contents, before update:
// 0xAAAAAAAA // selector
// 0x000: 0x________________________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB // account
// 0x020: 0x________________________________________________________CCCCCCCC // entityId
// 0x040: 0x________________________DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD // msg.sender
// 0x060: 0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE // hash
// 0x080: 0x______________________________________________________________a0 // signature offset
// 0x0a0... // dynamic fields
// Prepare the buffer for pre-signature validation hooks.
_prepareSigValidationCallBufferPreSigValidationHooks(result);
}
function invokePreSignatureValidationHook(
SigCallBuffer buffer,
HookConfig hookEntity,
bytes calldata signatureSegment
) internal view {
bool success;
address moduleAddress;
uint32 entityId;
assembly ("memory-safe") {
// Load the module address and entity id
entityId := and(shr(64, hookEntity), 0xffffffff)
moduleAddress := shr(96, hookEntity)
// Update the buffer with the entity Id
mstore(add(buffer, 0x44), entityId)
// Copy in the signature segment
// Since the buffer's copy of the signature exceeds the length of any sub-segments, we can safely write
// over it.
mstore(add(buffer, 0xc4), signatureSegment.length)
// If there is a nonzero signature segment length, copy in the data.
if signatureSegment.length {
// Because we will be sending the data with word-aligned padding ("strict ABI encoding"), we need
// to zero out the last word of the buffer to prevent sending garbage data.
let roundedDownSignatureLength := and(signatureSegment.length, not(0x1f))
mstore(add(add(buffer, 0xe4), roundedDownSignatureLength), 0)
// Copy in the data
calldatacopy(add(buffer, 0xe4), signatureSegment.offset, signatureSegment.length)
}
// The data amount we actually want to call with is:
// 0xa4 (4 byte selector + 5 words of data: entity id, sender, hash, signature offset, signature
// length) + word-align(signature length)
let actualCallLength := add(0xa4, and(add(signatureSegment.length, 0x1f), not(0x1f)))
// Perform the call
success :=
staticcall(
gas(),
moduleAddress,
/*argOffset*/
add(buffer, 0x40), // jump over 32 bytes for length, and another 32 bytes for the account
/*argSize*/
actualCallLength,
/*retOffset*/
0,
/*retSize*/
0x20
)
}
if (!success) {
_revertModuleFunction(uint32(PreSignatureValidationHookReverted.selector), moduleAddress, entityId);
}
}
function invokeSignatureValidation(
SigCallBuffer buffer,
ModuleEntity validationFunction,
bytes calldata signatureSegment
) internal view returns (bytes4 result) {
bool success;
address moduleAddress;
uint32 entityId;
assembly ("memory-safe") {
// Load the module address and entity id
entityId := and(shr(64, validationFunction), 0xffffffff)
moduleAddress := shr(96, validationFunction)
// Store the account in the `account` field.
mstore(add(buffer, 0x24), address())
// Update the buffer with the entity Id
mstore(add(buffer, 0x44), entityId)
// Fix the calldata offsets of `signature`, due to including the `account` field for signature
// validation.
mstore(add(buffer, 0xa4), 0xa0)
// Copy in the signature segment
// Since the buffer's copy of the signature exceeds the length of any sub-segments, we can safely write
// over it.
mstore(add(buffer, 0xc4), signatureSegment.length)
// If there is a nonzero signature segment length, copy in the data.
if signatureSegment.length {
// Because we will be sending the data with word-aligned padding ("strict ABI encoding"), we need
// to zero out the last word of the buffer to prevent sending garbage data.
let roundedDownSignatureLength := and(signatureSegment.length, not(0x1f))
mstore(add(add(buffer, 0xe4), roundedDownSignatureLength), 0)
// Copy in the data
calldatacopy(add(buffer, 0xe4), signatureSegment.offset, signatureSegment.length)
}
// The data amount we actually want to call with is:
// 0xc4 (4 byte selector + 6 words of data: account, entity id, sender, hash, signature offset,
// signature length) + word-align(signature length)
let actualCallLength := add(0xc4, and(add(signatureSegment.length, 0x1f), not(0x1f)))
// Perform the call
success :=
and(
// Yul evaluates expressions from right to left, so `returndatasize` will evaluate after
// `staticcall`.
gt(returndatasize(), 0x1f),
staticcall(
gas(),
moduleAddress,
/*argOffset*/
add(buffer, 0x20), // jump over 32 bytes for length, and another 32 bytes for the account
/*argSize*/
actualCallLength,
/*retOffset*/
0,
/*retSize*/
0x20
)
)
}
if (success) {
assembly ("memory-safe") {
// Otherwise, we return the first word of the return data as the signature validation result
result := mload(0)
// If any of the lower 28 bytes are nonzero, it would be an abi decoding failure.
if shl(32, result) { revert(0, 0) }
}
} else {
_revertModuleFunction(uint32(SignatureValidationFunctionReverted.selector), moduleAddress, entityId);
}
}
/// @notice Appends a post hook to run to the dense post hook data buffer.
/// @param workingMemPtr The current working memory pointer
/// @param hookConfig The hook configuration
/// @param returnedBytesSize The size of the returned bytes from the pre hook
/// @return The new working memory pointer
function _appendPostHookToRun(bytes32 workingMemPtr, HookConfig hookConfig, uint256 returnedBytesSize)
private
pure
returns (bytes32)
{
// Each segment starts out at a length of 4 words:
// - post hook address
// - post hook entity Id
// - fixed preExecHookData offset (always 0x40)
// - preHookReturnData length
// Add to this the word-aligned length of the pre hook return data.
uint256 segmentLength = 0x80;
assembly ("memory-safe") {
// Load the module address and entity Id
let entityId := and(shr(64, hookConfig), 0xffffffff)
let moduleAddress := shr(96, hookConfig)
// Get the word-aligned data to copy length
let alignedDataLength := and(add(returnedBytesSize, 0x1f), not(0x1f))
segmentLength := add(segmentLength, alignedDataLength)
// Start writing to memory:
// Store the post hook address
mstore(workingMemPtr, moduleAddress)
workingMemPtr := add(workingMemPtr, 0x20)
// Store the post hook entity Id
mstore(workingMemPtr, entityId)
workingMemPtr := add(workingMemPtr, 0x20)
// Store the fixed preExecHookData offset
mstore(workingMemPtr, 0x40)
workingMemPtr := add(workingMemPtr, 0x20)
// Store the preHookReturnData length
mstore(workingMemPtr, returnedBytesSize)
workingMemPtr := add(workingMemPtr, 0x20)
// Copy in the pre hook return data, if any exists
if returnedBytesSize {
// Zero out the last memory word to encode in strict ABI mode
let roundedDownDataLength := and(returnedBytesSize, not(0x1f))
mstore(add(workingMemPtr, roundedDownDataLength), 0)
// Copy in the data
returndatacopy(workingMemPtr, 0x40, returnedBytesSize)
workingMemPtr := add(workingMemPtr, alignedDataLength)
}
// Store the overall segment length at the end
mstore(workingMemPtr, segmentLength)
workingMemPtr := add(workingMemPtr, 0x20)
}
return workingMemPtr;
}
function _revertModuleFunction(uint32 errorSelector, address moduleAddress, uint32 entityId) private pure {
// All of the module function reverts have the same parameter layout:
// - module address
// - entity Id
// - revert data
assembly ("memory-safe") {
let m := mload(0x40)
// Write in order of entityId -> address -> selector, to avoid masking or shifts.
mstore(add(m, 0x18), entityId)
mstore(add(m, 0x14), moduleAddress)
mstore(m, errorSelector)
mstore(add(m, 0x40), 0x40) // fixed offset for the revert data
mstore(add(m, 0x60), returndatasize())
if returndatasize() {
// Store a zero in the last word of the revert data, to do strict ABI-encoding for the error.
let roundedDownDataLength := and(returndatasize(), not(0x1f))
mstore(add(m, add(0x80, roundedDownDataLength)), 0)
returndatacopy(add(m, 0x80), 0, returndatasize())
}
let roundedUpDataLength := and(add(returndatasize(), 0x1f), not(0x1f))
// 4 bytes for the selector, and 0x60 for the 3 words of fixed-size data.
let totalRevertDataLength := add(0x64, roundedUpDataLength)
revert(add(m, 0x1c), totalRevertDataLength)
}
}
function _prepareRuntimeCallBufferPreValidationHooks(RTCallBuffer buffer) private pure {
uint32 selector = uint32(IValidationHookModule.preRuntimeValidationHook.selector);
assembly ("memory-safe") {
// Update the buffer with the selector. This will squash a portion of the `account` param for runtime
// validation, but that will be restored before calling.
mstore(add(buffer, 0x24), selector)
// Fix the calldata offsets of `callData` and `authorization`, due to excluding the `account` field.
// The offset of calldata starts out as 0x0c0, but for pre-validation hooks, it should be 0x0a0.
mstore(add(buffer, 0xa4), 0xa0)
// The offset of authorization should be decremented by one word.
let authorizationOffsetPtr := add(buffer, 0xc4)
// Get the stored value. This will be wrong for preRuntimeValidationHooks, because the buffer size is
// smaller by 1 word.
let authorizationOffset := mload(authorizationOffsetPtr)
// Fix the stored offset value
mstore(authorizationOffsetPtr, sub(authorizationOffset, 0x20))
}
}
function _prepareSigValidationCallBufferPreSigValidationHooks(SigCallBuffer buffer) private pure {
uint32 selector = uint32(IValidationHookModule.preSignatureValidationHook.selector);
assembly ("memory-safe") {
// Update the buffer with the selector. This will squash a portion of the `account` param for signature
// validation, but that will be restored before calling.
mstore(add(buffer, 0x24), selector)
// Fix the calldata offset of `signature`, due to excluding the `account` field.
// The offset of the signature starts out as 0xa0, but for pre-validation hooks, it should be 0x80.
mstore(add(buffer, 0xa4), 0x80)
}
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {HookConfig} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {ExecutionStorage, ValidationStorage} from "../account/AccountStorage.sol";
import {LinkedListSet, LinkedListSetLib, SENTINEL_VALUE, SetValue} from "./LinkedListSetLib.sol";
type MemSnapshot is uint256;
/// @title Memory Management Library
/// @author Alchemy
/// @notice A library for managing memory in ModularAccount. Handles loading data from storage into memory, and
/// manipulating the free memory pointer.
library MemManagementLib {
/// @notice Load execution hooks associated both with a validation function and an execution selector.
/// @param execData The execution storage struct to load from.
/// @param valData The validation storage struct to load from.
/// @return hooks An array of `HookConfig` items, representing the execution hooks.
function loadExecHooks(ExecutionStorage storage execData, ValidationStorage storage valData)
internal
view
returns (HookConfig[] memory hooks)
{
// Load selector-assoc hooks first, then validation-assoc, because execution order is reversed
// This next code segment is adapted from the function LinkedListSetLib.getAll.
mapping(bytes32 => bytes32) storage llsMap = execData.executionHooks.map;
uint256 size = 0;
bytes32 cursor = llsMap[SENTINEL_VALUE];
// Dynamically allocate the returned array as we iterate through the set, since we don't know the size
// beforehand.
// This is accomplished by first writing to memory after the free memory pointer,
// then updating the free memory pointer to cover the newly-allocated data.
// To the compiler, writes to memory after the free memory pointer are considered "memory safe".
// See https://docs.soliditylang.org/en/v0.8.22/assembly.html#memory-safety
// Stack variable lifting done when compiling with via-ir will only ever place variables into memory
// locations below the current free memory pointer, so it is safe to compile this library with via-ir.
// See https://docs.soliditylang.org/en/v0.8.22/yul.html#memoryguard
assembly ("memory-safe") {
// It is critical that no other memory allocations occur between:
// - loading the value of the free memory pointer into `ret`
// - updating the free memory pointer to point to the newly-allocated data, which is done after all
// the values have been written.
hooks := mload(0x40)
}
while (!LinkedListSetLib.isSentinel(cursor) && cursor != bytes32(0)) {
unchecked {
++size;
}
// Place the item into the return array manually. Since the size was just incremented, it will point to
// the next location to write to.
assembly ("memory-safe") {
mstore(add(hooks, mul(size, 0x20)), cursor)
}
cursor = llsMap[cursor];
}
// Load validation-assoc hooks
uint256 validationAssocHooksLength = valData.executionHookCount;
llsMap = valData.executionHooks.map;
// Notably, we invert the mapping lookup ordering for validation-assoc hooks, because we know the length
// ahead-of-time, thus saving an `sload`. This is why the cursor starts at SENTINEL_VALUE.
cursor = SENTINEL_VALUE;
for (uint256 i = 0; i < validationAssocHooksLength; ++i) {
unchecked {
++size;
}
cursor = llsMap[cursor];
assembly ("memory-safe") {
mstore(add(hooks, mul(size, 0x20)), cursor)
}
}
assembly ("memory-safe") {
// Update the free memory pointer with the now-known length of the array.
mstore(0x40, add(hooks, mul(add(size, 1), 0x20)))
// Set the length of the array.
mstore(hooks, size)
}
return hooks;
}
/// @notice Load execution hooks associated with an execution selector.
/// @param execData The execution storage struct to load from.
/// @return hooks An array of `HookConfig` items, representing the execution hooks.
function loadExecHooks(ExecutionStorage storage execData) internal view returns (HookConfig[] memory) {
HookConfig[] memory hooks;
SetValue[] memory hooksSet = LinkedListSetLib.getAll(execData.executionHooks);
// SetValue is internally a bytes31, and HookConfig is a bytes25, which are both left-aligned. This cast is
// safe so long as only HookConfig entries are added to the set.
assembly ("memory-safe") {
hooks := hooksSet
}
return hooks;
}
/// @notice Load execution hooks associated with a validation function.
/// @param valData The validation storage struct to load from.
/// @return hooks An array of `HookConfig` items, representing the execution hooks.
function loadExecHooks(ValidationStorage storage valData) internal view returns (HookConfig[] memory) {
uint256 validationAssocHooksLength = valData.executionHookCount;
return _loadValidationAssociatedHooks(validationAssocHooksLength, valData.executionHooks);
}
/// @notice Load validation hooks associated with a validation function.
/// @param valData The validation storage struct to load from.
/// @return hooks An array of `HookConfig` items, representing the validation hooks.
function loadValidationHooks(ValidationStorage storage valData) internal view returns (HookConfig[] memory) {
uint256 validationHookCount = valData.validationHookCount;
return _loadValidationAssociatedHooks(validationHookCount, valData.validationHooks);
}
/// @notice Load all selectors that have been added to a validation function.
/// @param valData The validation storage struct to load from.
/// @return selectors An array of the selectors the validation function is allowed to validate.
function loadSelectors(ValidationStorage storage valData) internal view returns (bytes4[] memory selectors) {
SetValue[] memory selectorsSet = LinkedListSetLib.getAll(valData.selectors);
// SetValue is internally a bytes31, and both bytes4 and bytes31 are left-aligned. This cast is safe so
// long as only bytes4 entries are added to the set.
assembly ("memory-safe") {
selectors := selectorsSet
}
return selectors;
}
/// @notice Reverses an array of `HookConfig` items in place.
function reverseArr(HookConfig[] memory hooks) internal pure {
bytes32[] memory casted;
// Cast to bytes32[] to use the shared reverseArr function
assembly ("memory-safe") {
casted := hooks
}
_reverseArr(casted);
}
/// @notice Reverses an array of `bytes4` items in place.
function reverseArr(bytes4[] memory selectors) internal pure {
bytes32[] memory casted;
// Cast to bytes32[] to use the shared reverseArr function
assembly ("memory-safe") {
casted := selectors
}
_reverseArr(casted);
}
/// @notice If the callData is an encoded function call to IModularAccount.execute, retrieves the target of the
/// call.
/// @param callData The calldata to check.
/// @return target The target of the call.
function getExecuteTarget(bytes calldata callData) internal pure returns (address) {
address target;
assembly ("memory-safe") {
target := and(calldataload(add(callData.offset, 4)), 0xffffffffffffffffffffffffffffffffffffffff)
}
return target;
}
/// @notice Captures a snapshot of the free memory pointer.
/// @return The snapshot of the free memory pointer.
function freezeFMP() internal pure returns (MemSnapshot) {
MemSnapshot snapshot;
assembly ("memory-safe") {
snapshot := mload(0x40)
}
return snapshot;
}
/// @notice Restores the free memory pointer to a previous snapshot.
/// @dev This invalidates any memory allocated since the snapshot was taken.
/// @param snapshot The snapshot to restore to.
function restoreFMP(MemSnapshot snapshot) internal pure {
assembly ("memory-safe") {
mstore(0x40, snapshot)
}
}
/// @notice Used to load both pre validation hooks and pre execution hooks, associated with a validation
/// function. The caller must first get the length of the hooks from the ValidationStorage struct.
function _loadValidationAssociatedHooks(uint256 hookCount, LinkedListSet storage hooks)
private
view
returns (HookConfig[] memory)
{
HookConfig[] memory hookArr = new HookConfig[](hookCount);
mapping(bytes32 => bytes32) storage llsMap = hooks.map;
bytes32 cursor = SENTINEL_VALUE;
for (uint256 i = 0; i < hookCount; ++i) {
cursor = llsMap[cursor];
hookArr[i] = HookConfig.wrap(bytes25(cursor));
}
return hookArr;
}
function _reverseArr(bytes32[] memory hooks) private pure {
uint256 len = hooks.length;
uint256 halfLen = len / 2;
for (uint256 i = 0; i < halfLen; ++i) {
uint256 j = len - i - 1;
(hooks[i], hooks[j]) = (hooks[j], hooks[i]);
}
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {DIRECT_CALL_VALIDATION_ENTITY_ID} from "@erc6900/reference-implementation/helpers/Constants.sol";
import {ModuleEntity, ModuleEntityLib} from "@erc6900/reference-implementation/libraries/ModuleEntityLib.sol";
import {
ValidationConfig,
ValidationConfigLib
} from "@erc6900/reference-implementation/libraries/ValidationConfigLib.sol";
import {ValidationStorage} from "../account/AccountStorage.sol";
// A type representing a validation lookup key and flags for validation options.
// The validation lookup key is a tagged union between a direct call validation address and a validation entity ID.
type ValidationLocator is uint168;
// Layout:
// Unused
// 0x0000000000000000000000__________________________________________
// Either the direct call validation's address, or the entity ID for non-direct-call validation.
// 0x______________________AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA__
// Validation options
// 0x______________________________________________________________BB
// ValidationOptions layout:
// 0b00000___ // Unused
// 0b_____A__ // is direct call validation (union tag)
// 0b______B_ // has deferred action
// 0b_______C // is global validation
// A type representing only the validation lookup key, with validation options masked out except for the
// direct call validation flag.
type ValidationLookupKey is uint168;
using ValidationLocatorLib for ValidationLocator global;
using ValidationLocatorLib for ValidationLookupKey global;
library ValidationLocatorLib {
using ValidationConfigLib for ValidationConfig;
uint8 internal constant _VALIDATION_TYPE_GLOBAL = 1;
uint8 internal constant _HAS_DEFERRED_ACTION = 2;
uint8 internal constant _IS_DIRECT_CALL_VALIDATION = 4;
function moduleEntity(ValidationLookupKey _lookupKey, ValidationStorage storage validationStorage)
internal
view
returns (ModuleEntity result)
{
if (_lookupKey.isDirectCallValidation()) {
result = ModuleEntityLib.pack(_lookupKey.directCallAddress(), DIRECT_CALL_VALIDATION_ENTITY_ID);
} else {
result = ModuleEntityLib.pack(validationStorage.module, _lookupKey.entityId());
}
}
// User op nonce, 4337 mandated layout:
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________ // Parallel Nonce Key
// 0x________________________________________________BBBBBBBBBBBBBBBB // Sequential Nonce Key
// User op nonce, Alchemy MA usage:
// With non-direct call validation
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA__________________________ // Parallel Nonce Key
// 0x______________________________________BBBBBBBB__________________ // Validation Entity ID
// 0x______________________________________________CC________________ // Options byte
// 0x________________________________________________BBBBBBBBBBBBBBBB // Sequential Nonce Key
// With direct call validation
// 0xAAAAAA__________________________________________________________ // Parallel Nonce Key
// 0x______BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB__________________ // Caller address of direct-call
// validation
// 0x______________________________________________CC________________ // Options byte
// 0x________________________________________________BBBBBBBBBBBBBBBB // Sequential Nonce Key
function loadFromNonce(uint256 nonce) internal pure returns (ValidationLocator result) {
assembly ("memory-safe") {
nonce := shr(64, nonce)
let validationType := and(nonce, _IS_DIRECT_CALL_VALIDATION)
switch validationType
case 0 {
// If not using direct call validation, the validation locator contains a 4-byte entity ID
// Mask it to the lower 5 bytes
result := and(nonce, 0xFFFFFFFFFF)
}
default {
// If using direct call validation, the validation locator contains a 20-byte address
// Mask it to the lower 21 bytes
result := and(nonce, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
}
}
}
// executeRuntimeValidation authorization layout, and isValidSignature signature layout
// [1-byte options][4-byte validation id OR 20-byte address of direct call validation][remainder]
// With non-direct call validation
// 0xAA______________ // Validation Type
// 0x__BBBBBBBB______ // Validation Entity ID
// 0x__________CCC... // Remainder
// With direct call validation
// 0xAA______________________________________________ // Validation Type
// 0x__BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB______ // Caller address of direct-call validation
// 0x__________________________________________CCC... // Remainder
function loadFromSignature(bytes calldata signature)
internal
pure
returns (ValidationLocator result, bytes calldata remainder)
{
assembly ("memory-safe") {
// Regular validation requires at least 5 bytes. Direct call validation requires at least 21 bytes,
// checked later.
if lt(signature.length, 5) { revert(0, 0) }
result := calldataload(signature.offset)
let validationOptions := shr(248, result)
switch and(validationOptions, _IS_DIRECT_CALL_VALIDATION)
case 0 {
// If not using direct call validation, the validation locator contains a 32-byte entity ID
// Result contains:
// 0xAA______________________________________________________________ // Validation Type
// 0x__BBBBBBBB______________________________________________________ // Validation Entity ID
// 0x__________CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC // Remainder bits and/or
// zeros
// We need to clear the upper byte by shifting left 1 bytes (8 bits), then shift right 28 bytes
// (224 bits), leaving only the entity ID.
result := shr(224, shl(8, result))
// Next, we need to set the validation type, which is 0 in this branch
result := or(shl(8, result), validationOptions)
// Advance the remainder by 5 bytes
remainder.offset := add(signature.offset, 5)
remainder.length := sub(signature.length, 5)
}
default {
// Direct call validation requires at least 21 bytes
if lt(signature.length, 21) { revert(0, 0) }
// If using direct call validation, the validation locator contains a 20-byte address
// Result contains:
// 0xAA______________________________________________________________ // Validation Type
// 0x__BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB______________________ // Caller address of
// direct-call validation
// 0x__________________________________________CCCCCCCCCCCCCCCCCCCCCC // Remainder bits and/or
// zeros
// So we need to clear the upper byte by shifting left 1 bytes (8 bits), then shift right 12
// bytes (96 bits) to get the address.
result := shr(96, shl(8, result))
// Next, we need to set the validation type
result := or(shl(8, result), validationOptions)
// Advance the remainder by 21 bytes
remainder.offset := add(signature.offset, 21)
remainder.length := sub(signature.length, 21)
}
}
}
// Only safe to call if the lookup has been asserted to be a direct call validation.
function directCallAddress(ValidationLookupKey _lookupKey) internal pure returns (address result) {
assembly ("memory-safe") {
result := and(shr(8, _lookupKey), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
}
}
// Only safe to call if the lookup has been asserted to be a non-direct call validation.
function entityId(ValidationLookupKey _lookupKey) internal pure returns (uint32 result) {
assembly ("memory-safe") {
result := and(shr(8, _lookupKey), 0xFFFFFFFF)
}
}
function isGlobal(ValidationLocator locator) internal pure returns (bool) {
return (ValidationLocator.unwrap(locator) & _VALIDATION_TYPE_GLOBAL) != 0;
}
function hasDeferredAction(ValidationLocator locator) internal pure returns (bool) {
return (ValidationLocator.unwrap(locator) & _HAS_DEFERRED_ACTION) != 0;
}
function isDirectCallValidation(ValidationLookupKey _lookupKey) internal pure returns (bool) {
return (ValidationLookupKey.unwrap(_lookupKey) & _IS_DIRECT_CALL_VALIDATION) != 0;
}
function configToLookupKey(ValidationConfig validationConfig)
internal
pure
returns (ValidationLookupKey result)
{
if (validationConfig.entityId() == DIRECT_CALL_VALIDATION_ENTITY_ID) {
result = ValidationLookupKey.wrap(
uint168(uint160(validationConfig.module())) << 8 | _IS_DIRECT_CALL_VALIDATION
);
} else {
result = ValidationLookupKey.wrap(uint168(uint160(validationConfig.entityId())) << 8);
}
}
function moduleEntityToLookupKey(ModuleEntity _moduleEntity)
internal
pure
returns (ValidationLookupKey result)
{
(address module, uint32 _entityId) = ModuleEntityLib.unpack(_moduleEntity);
if (_entityId == DIRECT_CALL_VALIDATION_ENTITY_ID) {
result = ValidationLookupKey.wrap(uint168(uint160(module)) << 8 | _IS_DIRECT_CALL_VALIDATION);
} else {
result = ValidationLookupKey.wrap(uint168(uint160(_entityId)) << 8);
}
}
function directCallLookupKey(address directCallValidation)
internal
pure
returns (ValidationLookupKey result)
{
result = ValidationLookupKey.wrap(uint168(uint160(directCallValidation)) << 8 | _IS_DIRECT_CALL_VALIDATION);
}
function lookupKey(ValidationLocator locator) internal pure returns (ValidationLookupKey result) {
assembly ("memory-safe") {
result := and(locator, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04)
}
}
// Packing functions. These should not be used in the account, but in scripts and tests.
function pack(uint32 _entityId, bool _isGlobal, bool _hasDeferredAction)
internal
pure
returns (ValidationLocator)
{
uint168 result = uint168(_entityId) << 8;
if (_isGlobal) {
result |= _VALIDATION_TYPE_GLOBAL;
}
if (_hasDeferredAction) {
result |= _HAS_DEFERRED_ACTION;
}
return ValidationLocator.wrap(result);
}
function packDirectCall(address directCallValidation, bool _isGlobal, bool _hasDeferredAction)
internal
pure
returns (ValidationLocator)
{
uint168 result = uint168(uint160(directCallValidation)) << 8 | _IS_DIRECT_CALL_VALIDATION;
if (_isGlobal) {
result |= _VALIDATION_TYPE_GLOBAL;
}
if (_hasDeferredAction) {
result |= _HAS_DEFERRED_ACTION;
}
return ValidationLocator.wrap(result);
}
function packNonce(uint32 validationEntityId, bool _isGlobal, bool _hasDeferredAction)
internal
pure
returns (uint256 result)
{
result = uint256(validationEntityId) << 8;
if (_isGlobal) {
result |= _VALIDATION_TYPE_GLOBAL;
}
if (_hasDeferredAction) {
result |= _HAS_DEFERRED_ACTION;
}
// Finally, shift left to make space for the sequential nonce key
result <<= 64;
}
function packNonceDirectCall(address directCallValidation, bool _isGlobal, bool _hasDeferredAction)
internal
pure
returns (uint256 result)
{
result = uint256(uint160(directCallValidation)) << 8 | _IS_DIRECT_CALL_VALIDATION;
if (_isGlobal) {
result |= _VALIDATION_TYPE_GLOBAL;
}
if (_hasDeferredAction) {
result |= _HAS_DEFERRED_ACTION;
}
// Finally, shift left to make space for the sequential nonce key
result <<= 64;
}
function packSignature(uint32 validationEntityId, bool _isGlobal, bytes memory signature)
internal
pure
returns (bytes memory result)
{
uint8 options = 0;
if (_isGlobal) {
options |= _VALIDATION_TYPE_GLOBAL;
}
return bytes.concat(abi.encodePacked(options, uint32(validationEntityId)), signature);
}
function packSignatureDirectCall(
address directCallValidation,
bool _isGlobal,
bool _hasDeferredAction,
bytes memory signature
) internal pure returns (bytes memory result) {
uint8 options = _IS_DIRECT_CALL_VALIDATION;
if (_isGlobal) {
options |= _VALIDATION_TYPE_GLOBAL;
}
if (_hasDeferredAction) {
options |= _HAS_DEFERRED_ACTION;
}
return bytes.concat(abi.encodePacked(options, uint160(directCallValidation)), signature);
}
// Converts a module entity to a locator.
function packFromModuleEntity(ModuleEntity _moduleEntity, bool _isGlobal, bool _hasDeferredAction)
internal
pure
returns (ValidationLocator)
{
uint168 result;
(address module, uint32 _entityId) = ModuleEntityLib.unpack(_moduleEntity);
if (_entityId == DIRECT_CALL_VALIDATION_ENTITY_ID) {
result = uint168(uint160(module)) << 8 | _IS_DIRECT_CALL_VALIDATION;
} else {
result = uint168(_entityId) << 8;
}
if (_isGlobal) {
result |= _VALIDATION_TYPE_GLOBAL;
}
if (_hasDeferredAction) {
result |= _HAS_DEFERRED_ACTION;
}
return ValidationLocator.wrap(result);
}
// Operators
function eq(ValidationLookupKey a, ValidationLookupKey b) internal pure returns (bool) {
return ValidationLookupKey.unwrap(a) == ValidationLookupKey.unwrap(b);
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {IAccount} from "@eth-infinitism/account-abstraction/interfaces/IAccount.sol";
import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
/// @title Account Base
/// @author Alchemy
/// @dev An optimized implementation of a base account contract for ERC-4337.
/// Provides a public view function for getting the EntryPoint address, but does not provide one for getting the
/// nonce. The nonce may be retrieved from the EntryPoint contract.
/// Implementing contracts should override the _validateUserOp function to provide account-specific validation
/// logic.
abstract contract AccountBase is IAccount {
IEntryPoint internal immutable _ENTRY_POINT;
error NotEntryPoint();
constructor(IEntryPoint _entryPoint) {
_ENTRY_POINT = _entryPoint;
}
/// @inheritdoc IAccount
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
external
override
returns (uint256 validationData)
{
_requireFromEntryPoint();
validationData = _validateUserOp(userOp, userOpHash);
// Pay the prefund if necessary.
assembly ("memory-safe") {
if missingAccountFunds {
// Ignore failure (it's EntryPoint's job to verify, not the account's).
pop(call(gas(), caller(), missingAccountFunds, codesize(), 0x00, codesize(), 0x00))
}
}
}
/// @notice Gets the entry point for this account
/// @return entryPoint The entry point for this account
function entryPoint() external view returns (IEntryPoint) {
return _ENTRY_POINT;
}
/// @notice Account-specific implementation of user op validation. Override this function to define the
/// account's validation logic.
function _validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
internal
virtual
returns (uint256 validationData);
/// @notice Revert if the sender is not the EntryPoint.
function _requireFromEntryPoint() internal view {
if (msg.sender != address(_ENTRY_POINT)) {
revert NotEntryPoint();
}
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {AccountStorage, getAccountStorage} from "./AccountStorage.sol";
/// @title Account Storage Initializable
/// @author Alchemy
/// @notice A contract mixin that provides the functionality of OpenZeppelin's Initializable contract, using the
/// custom storage layout defined by the AccountStorage struct.
/// @dev The implementation logic here is modified from OpenZeppelin's Initializable contract from v5.0.
abstract contract AccountStorageInitializable {
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/// @notice Modifier to put on function intended to be called only once per implementation
/// @dev Reverts if the contract has already been initialized
modifier initializer() {
AccountStorage storage $ = getAccountStorage();
// 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);
}
}
/// @notice Internal function to disable calls to initialization functions
/// @dev Reverts if the contract is currently initializing.
function _disableInitializers() internal virtual {
AccountStorage storage $ = getAccountStorage();
if ($.initializing) {
revert InvalidInitialization();
}
if ($.initialized != type(uint8).max) {
$.initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {
HookConfig,
IModularAccount,
ModuleEntity
} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {
ExecutionDataView,
IModularAccountView,
ValidationDataView
} from "@erc6900/reference-implementation/interfaces/IModularAccountView.sol";
import {IAccountExecute} from "@eth-infinitism/account-abstraction/interfaces/IAccountExecute.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol";
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IModularAccountBase} from "../interfaces/IModularAccountBase.sol";
import {MemManagementLib} from "../libraries/MemManagementLib.sol";
import {ValidationLocatorLib} from "../libraries/ValidationLocatorLib.sol";
import {AccountBase} from "./AccountBase.sol";
import {ExecutionStorage, ValidationStorage, getAccountStorage} from "./AccountStorage.sol";
/// @title Modular Account View
/// @author Alchemy
/// @notice This abstract contract implements the two view functions to get validation and execution data for an
/// account.
abstract contract ModularAccountView is IModularAccountView {
/// @inheritdoc IModularAccountView
function getExecutionData(bytes4 selector) external view override returns (ExecutionDataView memory data) {
ExecutionStorage storage executionStorage = getAccountStorage().executionStorage[selector];
if (_isNativeFunction(uint32(selector))) {
bool isGlobalValidationAllowed = _isGlobalValidationAllowedNativeFunction(uint32(selector));
data.module = address(this);
data.skipRuntimeValidation = !isGlobalValidationAllowed;
data.allowGlobalValidation = isGlobalValidationAllowed;
if (!_isWrappedNativeFunction(uint32(selector))) {
// The native function does not run execution hooks associated with its selector, so
// we can return early.
return data;
}
} else {
data.module = executionStorage.module;
data.skipRuntimeValidation = executionStorage.skipRuntimeValidation;
data.allowGlobalValidation = executionStorage.allowGlobalValidation;
}
HookConfig[] memory hooks = MemManagementLib.loadExecHooks(executionStorage);
MemManagementLib.reverseArr(hooks);
data.executionHooks = hooks;
}
/// @inheritdoc IModularAccountView
function getValidationData(ModuleEntity validationFunction)
external
view
override
returns (ValidationDataView memory data)
{
ValidationStorage storage validationStorage =
getAccountStorage().validationStorage[ValidationLocatorLib.moduleEntityToLookupKey(validationFunction)];
data.validationFlags = validationStorage.validationFlags;
data.validationHooks = MemManagementLib.loadValidationHooks(validationStorage);
MemManagementLib.reverseArr(data.validationHooks);
HookConfig[] memory hooks = MemManagementLib.loadExecHooks(validationStorage);
MemManagementLib.reverseArr(hooks);
data.executionHooks = hooks;
bytes4[] memory selectors = MemManagementLib.loadSelectors(validationStorage);
MemManagementLib.reverseArr(selectors);
data.selectors = selectors;
}
function _isNativeFunction(uint32 selector) internal pure virtual returns (bool) {
return (
_isGlobalValidationAllowedNativeFunction(selector)
|| selector == uint32(AccountBase.entryPoint.selector)
|| selector == uint32(AccountBase.validateUserOp.selector)
|| selector == uint32(IERC1155Receiver.onERC1155BatchReceived.selector)
|| selector == uint32(IERC1155Receiver.onERC1155Received.selector)
|| selector == uint32(IERC1271.isValidSignature.selector)
|| selector == uint32(IERC165.supportsInterface.selector)
|| selector == uint32(IERC721Receiver.onERC721Received.selector)
|| selector == uint32(IModularAccount.accountId.selector)
|| selector == uint32(IModularAccountView.getExecutionData.selector)
|| selector == uint32(IModularAccountView.getValidationData.selector)
|| selector == uint32(UUPSUpgradeable.proxiableUUID.selector)
);
}
/// @dev Check whether a function is a native function that allows global validation.
function _isGlobalValidationAllowedNativeFunction(uint32 selector) internal pure virtual returns (bool) {
return (
_isWrappedNativeFunction(selector) || selector == uint32(IAccountExecute.executeUserOp.selector)
|| selector == uint32(IModularAccount.executeWithRuntimeValidation.selector)
);
}
/// @dev Check whether a function is a native function that has the `wrapNativeFunction` modifier applied,
/// which means it runs execution hooks associated with its selector.
function _isWrappedNativeFunction(uint32 selector) internal pure virtual returns (bool) {
return (
selector == uint32(IModularAccount.execute.selector)
|| selector == uint32(IModularAccount.executeBatch.selector)
|| selector == uint32(IModularAccount.installExecution.selector)
|| selector == uint32(IModularAccount.installValidation.selector)
|| selector == uint32(IModularAccount.uninstallExecution.selector)
|| selector == uint32(IModularAccount.uninstallValidation.selector)
|| selector == uint32(IModularAccountBase.performCreate.selector)
|| selector == uint32(UUPSUpgradeable.upgradeToAndCall.selector)
);
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {MAX_VALIDATION_ASSOC_HOOKS} from "@erc6900/reference-implementation/helpers/Constants.sol";
import {IExecutionHookModule} from "@erc6900/reference-implementation/interfaces/IExecutionHookModule.sol";
import {
HookConfig,
IModularAccount,
ModuleEntity,
ValidationConfig,
ValidationFlags
} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {IValidationHookModule} from "@erc6900/reference-implementation/interfaces/IValidationHookModule.sol";
import {IValidationModule} from "@erc6900/reference-implementation/interfaces/IValidationModule.sol";
import {HookConfigLib} from "@erc6900/reference-implementation/libraries/HookConfigLib.sol";
import {ModuleEntityLib} from "@erc6900/reference-implementation/libraries/ModuleEntityLib.sol";
import {ValidationConfigLib} from "@erc6900/reference-implementation/libraries/ValidationConfigLib.sol";
import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol";
import {MemManagementLib} from "../libraries/MemManagementLib.sol";
import {ModuleInstallCommonsLib} from "../libraries/ModuleInstallCommonsLib.sol";
import {ValidationLocatorLib} from "../libraries/ValidationLocatorLib.sol";
import {ValidationStorage, getAccountStorage, toSetValue} from "./AccountStorage.sol";
/// @title Module Manager Internals
/// @author Alchemy
/// @notice This abstract contract hosts the internal installation and uninstallation methods of execution and
/// validation functions. Methods here update the account storage.
abstract contract ModuleManagerInternals is IModularAccount {
using LinkedListSetLib for LinkedListSet;
using ModuleEntityLib for ModuleEntity;
using ValidationConfigLib for ValidationConfig;
using HookConfigLib for HookConfig;
error ArrayLengthMismatch();
error PreValidationHookDuplicate();
error ValidationEntityIdInUse();
error ValidationAlreadySet(bytes4 selector, ModuleEntity validationFunction);
error ValidationAssocHookLimitExceeded();
function _setValidationFunction(
ValidationStorage storage validationStorage,
ValidationConfig validationConfig,
bytes4[] calldata selectors
) internal {
// To allow for flag updates and appending hooks and selectors, two cases should be considered:
// - stored module address is zero - store the new validation module address
// - stored module address already holds the address of the validation module being installed - update
// flags and selectors.
// If the stored module address does not match, revert, as the validation entity ID must be unique over the
// account.
address storedAddress = validationStorage.module;
(address moduleAddress,, ValidationFlags validationFlags) = validationConfig.unpackUnderlying();
if (storedAddress == address(0)) {
validationStorage.module = moduleAddress;
} else if (storedAddress != moduleAddress) {
revert ValidationEntityIdInUse();
}
validationStorage.validationFlags = validationFlags;
uint256 length = selectors.length;
for (uint256 i = 0; i < length; ++i) {
bytes4 selector = selectors[i];
if (!validationStorage.selectors.tryAdd(toSetValue(selector))) {
revert ValidationAlreadySet(selector, validationConfig.moduleEntity());
}
}
}
function _removeValidationFunction(ValidationStorage storage validationStorage) internal {
validationStorage.module = address(0);
validationStorage.validationFlags = ValidationFlags.wrap(0);
validationStorage.validationHookCount = 0;
validationStorage.executionHookCount = 0;
}
function _installValidation(
ValidationConfig validationConfig,
bytes4[] calldata selectors,
bytes calldata installData,
bytes[] calldata hooks
) internal {
ValidationStorage storage _validationStorage =
getAccountStorage().validationStorage[ValidationLocatorLib.configToLookupKey(validationConfig)];
_setValidationFunction(_validationStorage, validationConfig, selectors);
uint256 length = hooks.length;
for (uint256 i = 0; i < length; ++i) {
HookConfig hookConfig = HookConfig.wrap(bytes25(hooks[i][:25]));
bytes calldata hookData = hooks[i][25:];
if (hookConfig.isValidationHook()) {
// Increment the stored length of validation hooks, and revert if the limit is exceeded.
// Safety:
// validationHookCount is uint8, so math operations here should never overflow
unchecked {
if (uint256(_validationStorage.validationHookCount) + 1 > MAX_VALIDATION_ASSOC_HOOKS) {
revert ValidationAssocHookLimitExceeded();
}
++_validationStorage.validationHookCount;
}
if (!_validationStorage.validationHooks.tryAdd(toSetValue(hookConfig))) {
revert PreValidationHookDuplicate();
}
ModuleInstallCommonsLib.onInstall(
hookConfig.module(), hookData, type(IValidationHookModule).interfaceId
);
} else {
// Hook is an execution hook
// Safety:
// validationHookCount is uint8, so math operations here should never overflow
unchecked {
if (uint256(_validationStorage.executionHookCount) + 1 > MAX_VALIDATION_ASSOC_HOOKS) {
revert ValidationAssocHookLimitExceeded();
}
++_validationStorage.executionHookCount;
}
ModuleInstallCommonsLib.addExecHooks(_validationStorage.executionHooks, hookConfig);
ModuleInstallCommonsLib.onInstall(
hookConfig.module(), hookData, type(IExecutionHookModule).interfaceId
);
}
}
ModuleInstallCommonsLib.onInstall(
validationConfig.module(), installData, type(IValidationModule).interfaceId
);
emit ValidationInstalled(validationConfig.module(), validationConfig.entityId());
}
function _uninstallValidation(
ModuleEntity validationFunction,
bytes calldata uninstallData,
bytes[] calldata hookUninstallDatas
) internal {
ValidationStorage storage _validationStorage =
getAccountStorage().validationStorage[ValidationLocatorLib.moduleEntityToLookupKey(validationFunction)];
bool onUninstallSuccess = true;
// Send `onUninstall` to hooks
if (hookUninstallDatas.length > 0) {
HookConfig[] memory execHooks = MemManagementLib.loadExecHooks(_validationStorage);
HookConfig[] memory validationHooks = MemManagementLib.loadValidationHooks(_validationStorage);
// If any uninstall data is provided, assert it is of the correct length.
if (hookUninstallDatas.length != validationHooks.length + execHooks.length) {
revert ArrayLengthMismatch();
}
// Hook uninstall data is provided in the order of pre validation hooks, then execution hooks.
uint256 hookIndex = 0;
uint256 length = validationHooks.length;
for (uint256 i = 0; i < length; ++i) {
bytes calldata hookData = hookUninstallDatas[hookIndex];
(address hookModule,) = ModuleEntityLib.unpack(validationHooks[i].moduleEntity());
onUninstallSuccess =
onUninstallSuccess && ModuleInstallCommonsLib.onUninstall(hookModule, hookData);
hookIndex++;
}
length = execHooks.length;
for (uint256 i = 0; i < length; ++i) {
bytes calldata hookData = hookUninstallDatas[hookIndex];
address hookModule = execHooks[i].module();
onUninstallSuccess =
onUninstallSuccess && ModuleInstallCommonsLib.onUninstall(hookModule, hookData);
hookIndex++;
}
}
// Clear all stored hooks. The lengths of the hooks are cleared in `_removeValidationFunction`.
_validationStorage.validationHooks.clear();
_validationStorage.executionHooks.clear();
// Clear selectors
_validationStorage.selectors.clear();
// Clear validation function data.
// Must be done at the end, because the hook lengths are accessed in the loop above.
_removeValidationFunction(_validationStorage);
(address module, uint32 entityId) = ModuleEntityLib.unpack(validationFunction);
onUninstallSuccess = onUninstallSuccess && ModuleInstallCommonsLib.onUninstall(module, uninstallData);
emit ValidationUninstalled(module, entityId, onUninstallSuccess);
}
}// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {IERC1155Receiver} from "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
/// @title Token Receiver
/// @author Alchemy
/// @notice Token receiver, supports tokens callbacks to allow the account to receive supported tokens.
abstract contract TokenReceiver is IERC721Receiver, IERC1155Receiver {
/// @inheritdoc IERC721Receiver
function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
/// @inheritdoc IERC1155Receiver
function onERC1155Received(address, address, uint256, uint256, bytes calldata)
external
pure
override
returns (bytes4)
{
return IERC1155Receiver.onERC1155Received.selector;
}
/// @inheritdoc IERC1155Receiver
function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata)
external
pure
override
returns (bytes4)
{
return IERC1155Receiver.onERC1155BatchReceived.selector;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import {IModule} from "./IModule.sol";
interface IExecutionHookModule is IModule {
/// @notice Run the pre execution hook specified by the `entityId`.
/// @dev To indicate the entire call should revert, the function MUST revert.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param sender The caller address.
/// @param value The call value.
/// @param data The calldata sent. For `executeUserOp` calls, hook modules should receive the full msg.data.
/// @return Context to pass to a post execution hook, if present. An empty bytes array MAY be returned.
function preExecutionHook(uint32 entityId, address sender, uint256 value, bytes calldata data)
external
returns (bytes memory);
/// @notice Run the post execution hook specified by the `entityId`.
/// @dev To indicate the entire call should revert, the function MUST revert.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param preExecHookData The context returned by its associated pre execution hook.
function postExecutionHook(uint32 entityId, bytes calldata preExecHookData) external;
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
import {IModule} from "./IModule.sol";
interface IValidationHookModule is IModule {
/// @notice Run the pre user operation validation hook specified by the `entityId`.
/// @dev Pre user operation validation hooks MUST NOT return an authorizer value other than 0 or 1.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param userOp The user operation.
/// @param userOpHash The user operation hash.
/// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes).
function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash)
external
returns (uint256);
/// @notice Run the pre runtime validation hook specified by the `entityId`.
/// @dev To indicate the entire call should revert, the function MUST revert.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param sender The caller address.
/// @param value The call value.
/// @param data The calldata sent.
/// @param authorization Additional data for the hook to use.
function preRuntimeValidationHook(
uint32 entityId,
address sender,
uint256 value,
bytes calldata data,
bytes calldata authorization
) external;
/// @notice Run the pre signature validation hook specified by the `entityId`.
/// @dev To indicate the call should revert, the function MUST revert.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param sender The caller address.
/// @param hash The hash of the message being signed.
/// @param signature The signature of the message.
function preSignatureValidationHook(uint32 entityId, address sender, bytes32 hash, bytes calldata signature)
external
view;
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
import {IModule} from "./IModule.sol";
interface IValidationModule is IModule {
/// @notice Run the user operation validation function specified by the `entityId`.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param userOp The user operation.
/// @param userOpHash The user operation hash.
/// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes).
function validateUserOp(uint32 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash)
external
returns (uint256);
/// @notice Run the runtime validation function specified by the `entityId`.
/// @dev To indicate the entire call should revert, the function MUST revert.
/// @param account the account to validate for.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param sender The caller address.
/// @param value The call value.
/// @param data The calldata sent.
/// @param authorization Additional data for the validation function to use.
function validateRuntime(
address account,
uint32 entityId,
address sender,
uint256 value,
bytes calldata data,
bytes calldata authorization
) external;
/// @notice Validates a signature using ERC-1271.
/// @dev To indicate the entire call should revert, the function MUST revert.
/// @param account the account to validate for.
/// @param entityId An identifier that routes the call to different internal implementations, should there
/// be more than one.
/// @param sender the address that sent the ERC-1271 request to the smart account
/// @param hash the hash of the ERC-1271 request
/// @param signature the signature of the ERC-1271 request
/// @return The ERC-1271 `MAGIC_VALUE` if the signature is valid, or 0xFFFFFFFF if invalid.
function validateSignature(
address account,
uint32 entityId,
address sender,
bytes32 hash,
bytes calldata signature
) external view returns (bytes4);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
import "./PackedUserOperation.sol";
/**
* The interface exposed by a paymaster contract, who agrees to pay the gas for user's operations.
* A paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction.
*/
interface IPaymaster {
enum PostOpMode {
// User op succeeded.
opSucceeded,
// User op reverted. Still has to pay for gas.
opReverted,
// Only used internally in the EntryPoint (cleanup after postOp reverts). Never calling paymaster with this value
postOpReverted
}
/**
* Payment validation: check if paymaster agrees to pay.
* Must verify sender is the entryPoint.
* Revert to reject this request.
* Note that bundlers will reject this method if it changes the state, unless the paymaster is trusted (whitelisted).
* The paymaster pre-pays using its deposit, and receive back a refund after the postOp method returns.
* @param userOp - The user operation.
* @param userOpHash - Hash of the user's request data.
* @param maxCost - The maximum cost of this transaction (based on maximum gas and gas price from userOp).
* @return context - Value to send to a postOp. Zero length to signify postOp is not required.
* @return validationData - Signature and time-range of this operation, encoded the same as the return
* value of validateUserOperation.
* <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
* other values are invalid for paymaster.
* <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
* <6-byte> validAfter - first timestamp this operation is valid
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) external returns (bytes memory context, uint256 validationData);
/**
* Post-operation handler.
* Must verify sender is the entryPoint.
* @param mode - Enum with the following options:
* opSucceeded - User operation succeeded.
* opReverted - User op reverted. The paymaster still has to pay for gas.
* postOpReverted - never passed in a call to postOp().
* @param context - The context value returned by validatePaymasterUserOp
* @param actualGasCost - Actual gas used so far (without this postOp call).
* @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas
* and maxPriorityFee (and basefee)
* It is not the same as tx.gasprice, which is what the bundler pays.
*/
function postOp(
PostOpMode mode,
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165Checker.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff;
/**
* @dev Returns true if `account` supports the {IERC165} interface.
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return
supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
!supportsERC165InterfaceUnchecked(account, INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
}
/**
* @dev Returns a boolean array where each value corresponds to the
* interfaces passed in and whether they're supported or not. This allows
* you to batch check interfaces for a contract where your expectation
* is that some interfaces may not be supported.
*
* See {IERC165-supportsInterface}.
*/
function getSupportedInterfaces(
address account,
bytes4[] memory interfaceIds
) internal view returns (bool[] memory) {
// an array of booleans corresponding to interfaceIds and whether they're supported or not
bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
// query support of ERC165 itself
if (supportsERC165(account)) {
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
}
}
return interfaceIdsSupported;
}
/**
* @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically.
*
* Batch-querying can lead to gas savings by skipping repeated checks for
* {IERC165} support.
*
* See {IERC165-supportsInterface}.
*/
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
// query support of ERC165 itself
if (!supportsERC165(account)) {
return false;
}
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
return false;
}
}
// all interfaces supported
return true;
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
*
* Some precompiled contracts will falsely indicate support for a given interface, so caution
* should be exercised when using this function.
*
* Interface identification is specified in ERC-165.
*/
function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
// prepare call
bytes memory encodedParams = abi.encodeCall(IERC165.supportsInterface, (interfaceId));
// perform static call
bool success;
uint256 returnSize;
uint256 returnValue;
assembly {
success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
returnSize := returndatasize()
returnValue := mload(0x00)
}
return success && returnSize >= 0x20 && returnValue > 0;
}
}// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.20; // Index marking the start of the data for the validation function. uint8 constant RESERVED_VALIDATION_DATA_INDEX = type(uint8).max; // Maximum number of validation-associated hooks that can be registered. uint8 constant MAX_VALIDATION_ASSOC_HOOKS = type(uint8).max; // Magic value for the Entity ID of direct call validation. uint32 constant DIRECT_CALL_VALIDATION_ENTITY_ID = type(uint32).max;
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import {HookConfig, ModuleEntity, ValidationFlags} from "../interfaces/IModularAccount.sol";
/// @dev Represents data associated with a specific function selector.
struct ExecutionDataView {
// The module that implements this execution function.
// If this is a native function, the address must be the address of the account.
address module;
// Whether or not the function needs runtime validation, or can be called by anyone. The function can still be
// state changing if this flag is set to true.
// Note that even if this is set to true, user op validation will still be required, otherwise anyone could
// drain the account of native tokens by wasting gas.
bool skipRuntimeValidation;
// Whether or not a global validation function may be used to validate this function.
bool allowGlobalValidation;
// The execution hooks for this function selector.
HookConfig[] executionHooks;
}
struct ValidationDataView {
// ValidationFlags layout:
// 0b00000___ // unused
// 0b_____A__ // isGlobal
// 0b______B_ // isSignatureValidation
// 0b_______C // isUserOpValidation
ValidationFlags validationFlags;
// The validation hooks for this validation function.
HookConfig[] validationHooks;
// Execution hooks to run with this validation function.
HookConfig[] executionHooks;
// The set of selectors that may be validated by this validation function.
bytes4[] selectors;
}
interface IModularAccountView {
/// @notice Get the execution data for a selector.
/// @dev If the selector is a native function, the module address will be the address of the account.
/// @param selector The selector to get the data for.
/// @return The execution data for this selector.
function getExecutionData(bytes4 selector) external view returns (ExecutionDataView memory);
/// @notice Get the validation data for a validation function.
/// @dev If the selector is a native function, the module address will be the address of the account.
/// @param validationFunction The validation function to get the data for.
/// @return The validation data for this validation function.
function getValidationData(ModuleEntity validationFunction)
external
view
returns (ValidationDataView memory);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
import "./PackedUserOperation.sol";
interface IAccount {
/**
* Validate user's signature and nonce
* the entryPoint will make the call to the recipient only if this validation call returns successfully.
* signature failure should be reported by returning SIG_VALIDATION_FAILED (1).
* This allows making a "simulation call" without a valid signature
* Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure.
*
* @dev Must validate caller is the entryPoint.
* Must validate the signature and nonce
* @param userOp - The operation that is about to be executed.
* @param userOpHash - Hash of the user's request data. can be used as the basis for signature.
* @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint.
* This is the minimum amount to transfer to the sender(entryPoint) to be
* able to make the call. The excess is left as a deposit in the entrypoint
* for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()".
* In case there is a paymaster in the request (or the current deposit is high
* enough), this value will be zero.
* @return validationData - Packaged ValidationData structure. use `_packValidationData` and
* `_unpackValidationData` to encode and decode.
* <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
* otherwise, an address of an "authorizer" contract.
* <6-byte> validUntil - Last timestamp this operation is valid. 0 for "indefinite"
* <6-byte> validAfter - First timestamp this operation is valid
* If an account doesn't use time-range, it is enough to
* return SIG_VALIDATION_FAILED value (1) for signature failure.
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "../../interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"@eth-infinitism/account-abstraction/=node_modules/account-abstraction/contracts/",
"account-abstraction/=node_modules/account-abstraction/contracts/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"@alchemy/light-account/=lib/light-account/",
"solady/=node_modules/solady/src/",
"@erc6900/reference-implementation/=node_modules/@erc6900/reference-implementation/src/",
"forge-gas-snapshot/=lib/forge-gas-snapshot/src/",
"forge-std/=lib/forge-std/src/",
"webauthn-sol/=lib/webauthn-sol/",
"FreshCryptoLib/=lib/webauthn-sol/lib/FreshCryptoLib/solidity/src/",
"openzeppelin-contracts/=lib/webauthn-sol/lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 50000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IEntryPoint","name":"entryPoint","type":"address"},{"internalType":"contract ExecutionInstallDelegate","name":"executionInstallDelegate","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CreateFailed","type":"error"},{"inputs":[],"name":"DeferredActionSignatureInvalid","type":"error"},{"inputs":[],"name":"DeferredValidationHasValidationHooks","type":"error"},{"inputs":[{"internalType":"HookConfig","name":"hookConfig","type":"bytes25"}],"name":"ExecutionHookAlreadySet","type":"error"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"InterfaceNotSupported","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"revertReason","type":"bytes"}],"name":"ModuleInstallCallbackFailed","type":"error"},{"inputs":[],"name":"NonCanonicalEncoding","type":"error"},{"inputs":[],"name":"NotEntryPoint","type":"error"},{"inputs":[],"name":"PreValidationHookDuplicate","type":"error"},{"inputs":[],"name":"RequireUserOperationContext","type":"error"},{"inputs":[],"name":"SegmentOutOfOrder","type":"error"},{"inputs":[],"name":"SelfCallRecursionDepthExceeded","type":"error"},{"inputs":[{"internalType":"ModuleEntity","name":"validationFunction","type":"bytes24"}],"name":"SignatureValidationInvalid","type":"error"},{"inputs":[],"name":"UnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"ModuleEntity","name":"validationFunction","type":"bytes24"},{"internalType":"address","name":"aggregator","type":"address"}],"name":"UnexpectedAggregator","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"UnrecognizedFunction","type":"error"},{"inputs":[],"name":"UpgradeFailed","type":"error"},{"inputs":[{"internalType":"ModuleEntity","name":"validationFunction","type":"bytes24"}],"name":"UserOpValidationInvalid","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"ModuleEntity","name":"validationFunction","type":"bytes24"}],"name":"ValidationAlreadySet","type":"error"},{"inputs":[],"name":"ValidationAssocHookLimitExceeded","type":"error"},{"inputs":[],"name":"ValidationEntityIdInUse","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"ValidationFunctionMissing","type":"error"},{"inputs":[],"name":"ValidationSignatureSegmentMissing","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"executionSelector","type":"bytes4"},{"internalType":"bool","name":"skipRuntimeValidation","type":"bool"},{"internalType":"bool","name":"allowGlobalValidation","type":"bool"}],"internalType":"struct ManifestExecutionFunction[]","name":"executionFunctions","type":"tuple[]"},{"components":[{"internalType":"bytes4","name":"executionSelector","type":"bytes4"},{"internalType":"uint32","name":"entityId","type":"uint32"},{"internalType":"bool","name":"isPreHook","type":"bool"},{"internalType":"bool","name":"isPostHook","type":"bool"}],"internalType":"struct ManifestExecutionHook[]","name":"executionHooks","type":"tuple[]"},{"internalType":"bytes4[]","name":"interfaceIds","type":"bytes4[]"}],"indexed":false,"internalType":"struct ExecutionManifest","name":"manifest","type":"tuple"}],"name":"ExecutionInstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"},{"indexed":false,"internalType":"bool","name":"onUninstallSucceeded","type":"bool"},{"components":[{"components":[{"internalType":"bytes4","name":"executionSelector","type":"bytes4"},{"internalType":"bool","name":"skipRuntimeValidation","type":"bool"},{"internalType":"bool","name":"allowGlobalValidation","type":"bool"}],"internalType":"struct ManifestExecutionFunction[]","name":"executionFunctions","type":"tuple[]"},{"components":[{"internalType":"bytes4","name":"executionSelector","type":"bytes4"},{"internalType":"uint32","name":"entityId","type":"uint32"},{"internalType":"bool","name":"isPreHook","type":"bool"},{"internalType":"bool","name":"isPostHook","type":"bool"}],"internalType":"struct ManifestExecutionHook[]","name":"executionHooks","type":"tuple[]"},{"internalType":"bytes4[]","name":"interfaceIds","type":"bytes4[]"}],"indexed":false,"internalType":"struct ExecutionManifest","name":"manifest","type":"tuple"}],"name":"ExecutionUninstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"},{"indexed":true,"internalType":"uint32","name":"entityId","type":"uint32"}],"name":"ValidationInstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"},{"indexed":true,"internalType":"uint32","name":"entityId","type":"uint32"},{"indexed":false,"internalType":"bool","name":"onUninstallSucceeded","type":"bool"}],"name":"ValidationUninstalled","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"accountId","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"contract IEntryPoint","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":[{"internalType":"bytes","name":"result","type":"bytes"}],"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 Call[]","name":"calls","type":"tuple[]"}],"name":"executeBatch","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes32","name":"accountGasLimits","type":"bytes32"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"bytes32","name":"gasFees","type":"bytes32"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct PackedUserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"executeUserOp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"authorization","type":"bytes"}],"name":"executeWithRuntimeValidation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"getExecutionData","outputs":[{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bool","name":"skipRuntimeValidation","type":"bool"},{"internalType":"bool","name":"allowGlobalValidation","type":"bool"},{"internalType":"HookConfig[]","name":"executionHooks","type":"bytes25[]"}],"internalType":"struct ExecutionDataView","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ModuleEntity","name":"validationFunction","type":"bytes24"}],"name":"getValidationData","outputs":[{"components":[{"internalType":"ValidationFlags","name":"validationFlags","type":"uint8"},{"internalType":"HookConfig[]","name":"validationHooks","type":"bytes25[]"},{"internalType":"HookConfig[]","name":"executionHooks","type":"bytes25[]"},{"internalType":"bytes4[]","name":"selectors","type":"bytes4[]"}],"internalType":"struct ValidationDataView","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ValidationConfig","name":"validationConfig","type":"bytes25"},{"internalType":"bytes4[]","name":"selectors","type":"bytes4[]"},{"internalType":"bytes","name":"installData","type":"bytes"},{"internalType":"bytes[]","name":"hooks","type":"bytes[]"}],"name":"initializeWithValidation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"module","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"executionSelector","type":"bytes4"},{"internalType":"bool","name":"skipRuntimeValidation","type":"bool"},{"internalType":"bool","name":"allowGlobalValidation","type":"bool"}],"internalType":"struct ManifestExecutionFunction[]","name":"executionFunctions","type":"tuple[]"},{"components":[{"internalType":"bytes4","name":"executionSelector","type":"bytes4"},{"internalType":"uint32","name":"entityId","type":"uint32"},{"internalType":"bool","name":"isPreHook","type":"bool"},{"internalType":"bool","name":"isPostHook","type":"bool"}],"internalType":"struct ManifestExecutionHook[]","name":"executionHooks","type":"tuple[]"},{"internalType":"bytes4[]","name":"interfaceIds","type":"bytes4[]"}],"internalType":"struct ExecutionManifest","name":"manifest","type":"tuple"},{"internalType":"bytes","name":"moduleInstallData","type":"bytes"}],"name":"installExecution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"ValidationConfig","name":"validationConfig","type":"bytes25"},{"internalType":"bytes4[]","name":"selectors","type":"bytes4[]"},{"internalType":"bytes","name":"installData","type":"bytes"},{"internalType":"bytes[]","name":"hooks","type":"bytes[]"}],"name":"installValidation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bool","name":"isCreate2","type":"bool"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"performCreate","outputs":[{"internalType":"address","name":"createdAddr","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"module","type":"address"},{"components":[{"components":[{"internalType":"bytes4","name":"executionSelector","type":"bytes4"},{"internalType":"bool","name":"skipRuntimeValidation","type":"bool"},{"internalType":"bool","name":"allowGlobalValidation","type":"bool"}],"internalType":"struct ManifestExecutionFunction[]","name":"executionFunctions","type":"tuple[]"},{"components":[{"internalType":"bytes4","name":"executionSelector","type":"bytes4"},{"internalType":"uint32","name":"entityId","type":"uint32"},{"internalType":"bool","name":"isPreHook","type":"bool"},{"internalType":"bool","name":"isPostHook","type":"bool"}],"internalType":"struct ManifestExecutionHook[]","name":"executionHooks","type":"tuple[]"},{"internalType":"bytes4[]","name":"interfaceIds","type":"bytes4[]"}],"internalType":"struct ExecutionManifest","name":"manifest","type":"tuple"},{"internalType":"bytes","name":"moduleUninstallData","type":"bytes"}],"name":"uninstallExecution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"ModuleEntity","name":"validationFunction","type":"bytes24"},{"internalType":"bytes","name":"uninstallData","type":"bytes"},{"internalType":"bytes[]","name":"hookUninstallData","type":"bytes[]"}],"name":"uninstallValidation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes32","name":"accountGasLimits","type":"bytes32"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"bytes32","name":"gasFees","type":"bytes32"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct PackedUserOperation","name":"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":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60e03461017357601f615c7838819003918201601f19168301916001600160401b03831184841017610178578084926040948552833981010312610173578051906001600160a01b038216820361017357602001516001600160a01b0381169190829003610173576080523060a0527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd005460ff8160401c166101625760fe196001600160401b038216016100ff575b5060c052604051615ae9908161018f8239608051818181610e3301528181611502015281816115c3015281816126360152612b3d015260a051818181610fbb0152611114015260c051816120ba0152f35b6001600160401b03191660ff9081177f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd00556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1386100ae565b63f92ee8a960e01b60005260046000fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361015610026575b36156100245761001c61252b565b602081519101f35b005b60003560e01c80621a63e91461014957806301ffc9a714610180578063150b7a021461017b5780631626ba7e1461017657806319822f7c146101715780631bbf564c1461016c57806334fcd5be146101675780634f1ef2861461016257806352d1902d1461015d5780635998db5c14610158578063757c8a26146101535780638dd7712f1461014e57806393b1dc61146101495780639cfd7cff14610144578063b0d691fe1461013f578063b61d27f61461013a578063b6b1ccfe14610135578063bc197c8114610130578063d31b575b1461012b578063e919a62314610126578063f23a6e61146101215763f2680c0f0361000e57611e64565b611dd3565b611b7b565b611ab7565b61193a565b61164b565b61153a565b6114b7565b61143a565b61027f565b611318565b611280565b611162565b6110e2565b610f5f565b610ddc565b610973565b610579565b6103a3565b610312565b6102c3565b73ffffffffffffffffffffffffffffffffffffffff8116036101a357565b600080fd5b35906101b382610185565b565b9181601f840112156101a35782359167ffffffffffffffff83116101a357602083818601950101116101a357565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101a35760043561021981610185565b9160243567ffffffffffffffff81116101a35760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82850301126101a357600401916044359067ffffffffffffffff82116101a35761027b916004016101b5565b9091565b346101a357610024610290366101e3565b9291909161209f565b7fffffffff000000000000000000000000000000000000000000000000000000008116036101a357565b346101a35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357602061030860043561030381610299565b6120f5565b6040519015158152f35b346101a35760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35761034c600435610185565b610357602435610185565b60643567ffffffffffffffff81116101a3576103779036906004016101b5565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b346101a35760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760243560043567ffffffffffffffff82116101a35761041461040e74ffffffffffffffffffffffffffffffffffffffff049336906004016101b5565b90612a20565b92919390931692610478838261047261046d8874ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b6135a3565b94613db1565b9082515b808015610519577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0191845191820391600183019081116105145782116105145761050f956104cf9260ff165b91613e20565b939296919093966105096104e385896123a6565b517fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001690565b86613f13565b61047c565b612abd565b610566610533888661052b8a88613fa4565b929091614014565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681529081906020820190565b0390f35b90816101209103126101a35790565b346101a35760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043567ffffffffffffffff81116101a3576105cb61064a91369060040161056a565b602435604435916105da612b26565b60009060208101356105eb816131d8565b6105f9610100840184612281565b81929192819461060b84600216151590565b6107aa575b50505061065760608501916106258387612281565b74ffffffffffffffffffffffffffffffffffffffff04839293169b8c92600116151590565b156107a2576000926137b6565b60ff6106b06106a68a74ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b5460b01c60ff1690565b1615159081610739575b5061070f576105669686956106ce9461442e565b90801561070857906106df916145ba565b915b6106f7575b506040519081529081906020820190565b60009081803892335af150386106e6565b50916106e1565b7f5f49f00a0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f8dd7712f00000000000000000000000000000000000000000000000000000000915061079961079361078d7fffffffff000000000000000000000000000000000000000000000000000000009388612281565b90612b8f565b90612c93565b161415386106ba565b6001926137b6565b65ffffffffffff97508293955061087c94506108769061084c6108706107ea6107e16107db61079389899a99612b8f565b60e01c90565b63ffffffff1690565b9a8b9361086961086388876108578c6108346107e16107db61079361081861081188612cf9565b8987612b9d565b98909761082d61082782612cf9565b91612d07565b9187612c7b565b9a8d6108518d61084c61084685612d07565b94612d07565b612d15565b92612c7b565b929091604051966142a0565b91604052565b1660a01b90565b99612d07565b91612c7b565b91388080610610565b9181601f840112156101a35782359167ffffffffffffffff83116101a3576020808501948460051b0101116101a357565b9060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101a3576004357fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000811681036101a3579160243567ffffffffffffffff81116101a3578161092d91600401610885565b9290929160443567ffffffffffffffff81116101a35781610950916004016101b5565b929092916064359067ffffffffffffffff82116101a35761027b91600401610885565b346101a357610981366108b6565b90919293946109ec61099161261c565b966109e461099e8a614604565b74ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b928984614689565b60005b828110610ab257610024878773ffffffffffffffffffffffffffffffffffffffff63ffffffff610a858d610a798c610a72610a6c7fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000085165b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690565b60601c90565b9687614b0f565b60401c63ffffffff1690565b1691167fc36a28045e90a1163d24d4216c8cfd8c44c4e835a486fb84d511d6b9e0736db9600080a361295f565b610af7610ad2610acc610ac6848789612d22565b90612bd8565b90612d39565b7fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001690565b90610b0d610b06828688612d22565b8091612be6565b9092670100000000000000811615610c8057845460a81c60ff169060ff60018184160111610c5657610b8b91610ad29060010160ff165b87547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1660a89190911b75ff00000000000000000000000000000000000000000016178755565b90610beb610be7610bde7fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000085165b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690565b60018801614b4a565b1590565b610c2c57600193610c21610a6c610c26947fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690565b614ad4565b016109ef565b7f298ac3da0000000000000000000000000000000000000000000000000000000060005260046000fd5b7fe330dd260000000000000000000000000000000000000000000000000000000060005260046000fd5b90610c90855460ff9060b01c1690565b9360ff60018187160111610c5657610d13610a6c610a47610d1895610d06610cbe60019a600160ff91011690565b8b547fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff1660b09190911b76ff0000000000000000000000000000000000000000000016178b55565b610ad28160028c016148db565b61497c565b610c26565b919082519283825260005b848110610d49575050601f19601f8460006020809697860101520116010190565b80602080928401015182828601015201610d28565b602081016020825282518091526040820191602060408360051b8301019401926000915b838310610d9157505050505090565b9091929394602080610dcd837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc086600196030187528951610d1d565b97019301930191939290610d82565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043567ffffffffffffffff81116101a357610e26903690600401610885565b610e2e61261c565b6060337f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1614610f0b5750610e778261235d565b9160005b818110610e9e5750506105669250610e929061295f565b60405191829182610d5e565b80610ee7610eb7610eb2600194868a612232565b612277565b6020610ec484878b612232565b013590610edf610ed585888c612232565b6040810190612281565b929091612f6b565b610eef612f80565b610ef982876123a6565b52610f0481866123a6565b5001610e7b565b91909260005b828110610f2757505050610e926105669261295f565b80610f59610f3b610eb26001948787612232565b6020610f48848888612232565b013590610edf610ed5858989612232565b01610f11565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357600435610f9581610185565b60243567ffffffffffffffff81116101a357610fb59036906004016101b5565b919091307f0000000000000000000000000000000000000000000000000000000000000000146110c957610fe761261c565b9073ffffffffffffffffffffffffffffffffffffffff60009316906352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602060016004601d865afa51036110bb57817fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8580a280827f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55611097575b836110948461295f565b80f35b908184926040519687378538925af4156110b257828061108a565b503d90823e3d90fd5b6355299b496001526004601dfd5b639f03a0266000526004601cfd5b60009103126101a357565b346101a35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357307f0000000000000000000000000000000000000000000000000000000000000000036110c95760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043560243567ffffffffffffffff81116101a3576111af9036906004016101b5565b906044359283151584036101a3576064356111c861261c565b94846040519485376001146112215750f05b8015611213576111ec6105669261295f565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b637e16b8cd6000526004601cfd5b929190f56111da565b906020808351928381520192019060005b8181106112485750505090565b82517fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001684526020938401939092019160010161123b565b346101a35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760606112c56004356112c081610299565b6123ba565b6105666040519283926020845273ffffffffffffffffffffffffffffffffffffffff81511660208501526020810151151560408501526040810151151582850152015160808084015260a083019061122a565b346101a35760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043567ffffffffffffffff81116101a35761136a61002491369060040161056a565b611372612b26565b61141b6114156113ed6113e874ffffffffffffffffffffffffffffffffffffffff046113a160208701356131d8565b1674ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b613208565b6000908051611420575b6114048261140f9261332a565b946060810190612281565b91613440565b306134e8565b61295f565b905061140f6114046114313661323e565b929150506113f7565b346101a35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357610566604080519061147b8183612322565b601d82527f616c6368656d792e6d6f64756c61722d6163636f756e742e322e302e30000000602083015251918291602083526020830190610d1d565b346101a35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b906020611537928181520190610d1d565b90565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043561157081610185565b6044359060243567ffffffffffffffff83116101a3576115ac61159a6105669436906004016101b5565b906115a361261c565b93606095612f6b565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303611607575b6115f39061295f565b604051918291602083526020830190610d1d565b90506115f3611614612f80565b9190506115ea565b600435907fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000821682036101a357565b346101a35760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35761168261161c565b60243567ffffffffffffffff81116101a3576116a29036906004016101b5565b9060443567ffffffffffffffff81116101a3576116c3903690600401610885565b90926116cd61261c565b936116da61099e87613514565b90600193806117e9575b50506100249561173d826116fd60016117529501614f39565b61170960028201614f39565b61171560038201614f39565b80547fffffffffffffffffff0000000000000000000000000000000000000000000000169055565b606081901c9160409190911c63ffffffff1690565b919093836117ae575b5050604051911515825263ffffffff169173ffffffffffffffffffffffffffffffffffffffff16907f43f7309d11ba6b2e180e9ab8a6da09d2fa1f585d7daed4b26c1c7f2a90b867a290602090a361295f565b7f43f7309d11ba6b2e180e9ab8a6da09d2fa1f585d7daed4b26c1c7f2a90b867a292935063ffffffff916117e29186614e85565b929161175b565b6117f283613208565b6117fb846135a3565b916118098351835190612d15565b810361191057919290600093805185915b81831061188f575050508051926000925b84841061183b57505050506116e4565b9091929394976118726001916118528b8686612d22565b90611863610a6c6104e38b8b6123a6565b8361187d575b505050996132fd565b95940192919061182b565b6118879350614e85565b388080611869565b909194929395986118f06001916118a78c878b612d22565b906118e061173d6118bb6104e38d8a6123a6565b7fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690565b50836118fe575b5050509a6132fd565b95019190959392949561181a565b6119089350614e85565b3880806118e7565b7fa24a13a60000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a35760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357611974600435610185565b61197f602435610185565b60443567ffffffffffffffff81116101a35761199f903690600401610885565b505060643567ffffffffffffffff81116101a3576119c1903690600401610885565b505060843567ffffffffffffffff81116101a3576119e39036906004016101b5565b50506040517fbc197c81000000000000000000000000000000000000000000000000000000008152602090f35b906020825260ff81511660208301526060611a52611a3d60208401516080604087015260a086019061122a565b6040840151601f19868303018487015261122a565b910151916080601f19828403019101526020808351928381520192019060005b818110611a7f5750505090565b82517fffffffff0000000000000000000000000000000000000000000000000000000016845260209384019390920191600101611a72565b346101a35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357610566611af161161c565b60405190611afe82612301565b60008252611b64600360208401606081526040850160608152611b4a611b2e61099e606089019760608952613514565b9260ff845460a01c168852611b42846135a3565b809152614c82565b611b5382613208565b90611b5d82614c82565b5201614c20565b90611b6e82614c82565b5260405191829182611a10565b346101a357611b89366108b6565b7f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0054969590949193919067ffffffffffffffff611bd660ff60408b901c16159967ffffffffffffffff1690565b1680159081611dcb575b6001149081611dc1575b159081611db8575b50611d8e57611c739688611c6a60017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000007f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd005416177f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0055565b611d1357612d9f565b611c7957005b611ce47fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0054167f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0055565b604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b611d89680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd005416177f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0055565b612d9f565b7ff92ee8a90000000000000000000000000000000000000000000000000000000060005260046000fd5b90501538611bf2565b303b159150611bea565b899150611be0565b346101a35760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357611e0d600435610185565b611e18602435610185565b60843567ffffffffffffffff81116101a357611e389036906004016101b5565b505060206040517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043567ffffffffffffffff81116101a357611eae9036906004016101b5565b60243567ffffffffffffffff81116101a35761040e611ed19136906004016101b5565b74ffffffffffffffffffffffffffffffffffffffff04839592931694611f43611f3a8774ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b91600116151590565b156120945790611f5760005b8787866137b6565b8094611fb38592611fab61046d8a74ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b968387613af3565b9385515b808015612029577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01938751918203916001830190811161051457821161051457612022986120089260ff166104c9565b9692999190969961201c6104e3888c6123a6565b89613b9a565b9392611fb7565b505061205e61205086956113e8610566988c6120488d6120649a613fa4565b929091615219565b60009080516120815761332a565b93613a0e565b61207561206f612f80565b9161295f565b60405191829182611526565b905061208e8484886139c5565b9061332a565b90611f576001611f4f565b505050506120ab61261c565b600060405136828237389036907f00000000000000000000000000000000000000000000000000000000000000005af4156120e9576101b39061295f565b6040513d6000823e3d90fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167fffffffff0000000000000000000000000000000000000000000000000000000081146121fd577f150b7a0200000000000000000000000000000000000000000000000000000000811480156121d4575b80156121ab575b6121a5576000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd03602052604060002054151590565b50600190565b507f01ffc9a700000000000000000000000000000000000000000000000000000000811461216f565b507f4e2312e0000000000000000000000000000000000000000000000000000000008114612168565b50600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b91908110156122725760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1813603018212156101a3570190565b612203565b3561153781610185565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101a3570180359067ffffffffffffffff82116101a3576020019181360383136101a357565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff82111761231d57604052565b6122d2565b90601f601f19910116810190811067ffffffffffffffff82111761231d57604052565b67ffffffffffffffff811161231d5760051b60200190565b9061236782612345565b6123746040519182612322565b828152601f196123848294612345565b019060005b82811061239557505050565b806060602080938501015201612389565b80518210156122725760209160051b010190565b90604051916123c883612301565b60008352600060208401526000604084015260608084015282612436827fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b9160e01c61244381612f9e565b1561249a57610be78161246e61245b612473946130ec565b3086528015602087015215156040860152565b61312c565b61249557506001612485915b01614c20565b61248e81614c82565b6060830152565b925050565b508161250a600192604061250361248596546124e66124cc8273ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff168552565b6124fa60a082901c60ff1615156020860152565b60a81c60ff1690565b1515910152565b61247f565b67ffffffffffffffff811161231d57601f01601f191660200190565b7fffffffff000000000000000000000000000000000000000000000000000000006000351673ffffffffffffffffffffffffffffffffffffffff6125ba827fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b54169081156125ef57506125e46125cf61261c565b916040519036825236600060208401376134e8565b61153761206f612f80565b7ffcfc5aad0000000000000000000000000000000000000000000000000000000060005260045260246000fd5b60009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633141580612955575b806128c6575b1561283b573360081b74ffffffffffffffffffffffffffffffffffffffff00166004179161269383366135b8565b6126e061046d8474ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b92835180612829575b805b6127e757506115379293506127c3906127bd6127767fffffffff00000000000000000000000000000000000000000000000000000000600035167fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b9174ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b90613c02565b60009181516127d3575b5061332a565b6127e0919250369061397c565b90386127cd565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0193846128223661281c6104e384866123a6565b86613b57565b90946126eb565b91506128353636613a32565b916126e9565b90611537906128c16128bc7fffffffff00000000000000000000000000000000000000000000000000000000600035167fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b6131cc565b6127c3565b50612950610be76129467fffffffff00000000000000000000000000000000000000000000000000000000600035167fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b5460a01c60ff1690565b612665565b503033141561265f565b601f19815191019060005b81811061297657505050565b8251809303928351601f196000602087015193635d413a8188527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4389101601c890183865af1950194156129ce57505060010161296a565b604051916018830152601482015263f19fc59381526040808201523d60608201523d612a06575b601c601f19601f3d01166064019101fd5b60006080601f193d16830101523d6000608083013e6129f5565b91600582106101a35782358060f81c9060048216600014612a8857601584106101a35760501c74ffffffffffffffffffffffffffffffffffffffff00161792601501917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb0190565b60d01c64ffffffff00161792600501917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161051457565b9190820391821161051457565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303612b6557565b7fd663742a0000000000000000000000000000000000000000000000000000000060005260046000fd5b906004116101a35790600490565b90929192836004116101a35783116101a357600401917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0190565b906019116101a35790601990565b90929192836019116101a35783116101a357601901917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe70190565b906015116101a35790601590565b90601b116101a35760150190600690565b9092919283601b116101a35783116101a357601b01917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe50190565b909392938483116101a35784116101a3578101920390565b919091357fffffffff0000000000000000000000000000000000000000000000000000000081169260048110612cc7575050565b7fffffffff00000000000000000000000000000000000000000000000000000000929350829060040360031b1b161690565b600401908160041161051457565b600801908160081161051457565b9190820180921161051457565b908210156122725761027b9160051b810190612281565b919091357fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000081169260198110612d6d575050565b7fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000929350829060190360031b1b161690565b94929591612dbb90612db361099e88614604565b928784614689565b60005b828110612e445750505050612e1a82610a7963ffffffff93610a72610a6c610a4773ffffffffffffffffffffffffffffffffffffffff987fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001690565b1691167fc36a28045e90a1163d24d4216c8cfd8c44c4e835a486fb84d511d6b9e0736db9600080a3565b612e58610ad2610acc610ac6848789612d22565b90612e67610b06828688612d22565b9092670100000000000000811615612f1057845460a81c60ff169060ff60018184160111610c5657612ea291610ad29060010160ff16610b44565b90612ed4610be7610bde7fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000008516610bb9565b610c2c57600193610c21610a6c612f0a947fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690565b01612dbe565b90612f20855460ff9060b01c1690565b9360ff60018187160111610c5657610d13610a6c610a47612f4e95610d06610cbe60019a600160ff91011690565b612f0a565b6000906101b3936040519381855260208501376134fe565b6101b3936040519381855260208501376134fe565b604051903d8252601f19603f3d840101166040523d6000602084013e565b612fa7816130ec565b9081156130d8575b81156130c4575b81156130b0575b811561309c575b8115613088575b8115613074575b8115613060575b811561304c575b8115613038575b8115613024575b8115613010575b8115612fff575090565b63e919a623915063ffffffff161490565b63ffffffff81166352d1902d149150612ff5565b63ffffffff811663d31b575b149150612fee565b63ffffffff811663757c8a26149150612fe7565b63ffffffff8116639cfd7cff149150612fe0565b63ffffffff811663150b7a02149150612fd9565b63ffffffff81166301ffc9a7149150612fd2565b63ffffffff8116631626ba7e149150612fcb565b63ffffffff811663f23a6e61149150612fc4565b63ffffffff811663bc197c81149150612fbd565b63ffffffff81166319822f7c149150612fb6565b63ffffffff811663b0d691fe149150612faf565b6130f58161312c565b908115613118575b8115613107575090565b63f2680c0f915063ffffffff161490565b63ffffffff8116638dd7712f1491506130fd565b63ffffffff1663b61d27f681149081156131be575b81156131b1575b81156131a3575b8115613195575b8115613187575b8115613179575b811561316e575090565b634f1ef28691501490565b635998db5c81149150613164565b63b6b1ccfe8114915061315d565b6393b1dc6181149150613156565b631bbf564c8114915061314f565b621a63e981149150613148565b6334fcd5be81149150613141565b60016115379101614c20565b60401c60048116156131fe5774ffffffffffffffffffffffffffffffffffffffffff1690565b64ffffffffff1690565b8054611537916002019060b01c60ff16614d04565b601f8260209493601f19938186528686013760008582860101520116010190565b611537613297916040519283917fed6dfb13000000000000000000000000000000000000000000000000000000006020840152600060248401523360448401523460648401526080608484015260a4830190600061321d565b03601f198101835282612322565b90611537906132976040519384927fed6dfb13000000000000000000000000000000000000000000000000000000006020850152600060248501523360448501523460648501526080608485015260a484019161321d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146105145760010190565b805160405193926000929091805b61334a57505050825260208201604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161337681836123a6565b517fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000081169067040000000000000016156133ea576133b48185614dea565b9067020000000000000081166133cd575b505080613338565b6133e3916133dd91849799614d89565b966132fd565b93386133c5565b6702000000000000008116613401575b5080613338565b6133dd61343a9183969860809063ffffffff6020938060601c835260401c16838201526040808201526000606082015201608081520190565b936133fa565b918215613482577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01908180845261347757505090565b600401602083013790565b8092506004116101a3577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201906134b98261250f565b926134c76040519485612322565b82845236818301116101a3576000926004601c930160208601378301015290565b600091389183602083519301915af1156120e957565b916000923892602083519301915af1156120e957565b74ffffffffffffffffffffffffffffffffffffffffff90606081901c9060401c63ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000018101613583575060081b74ffffffffffffffffffffffffffffffffffffffff00166004171690565b74ffffffffffffffffffffffffffffffffffffffff00915060081b161690565b8054611537916001019060a81c60ff16614d04565b60009160048210613758576135cd8284612c93565b7f8dd7712f000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821614613714575b806136456002847fffffffff0000000000000000000000000000000000000000000000000000000094614fb4565b167fb61d27f60000000000000000000000000000000000000000000000000000000081036136d3575050506004013573ffffffffffffffffffffffffffffffffffffffff165b73ffffffffffffffffffffffffffffffffffffffff1630146136a957565b7f54ff929d0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f34fcd5be00000000000000000000000000000000000000000000000000000000146136fe57505050565b61370e826002936101b395612b9d565b90615076565b5091908061372192612b9d565b90917fffffffff000000000000000000000000000000000000000000000000000000006137516107938486612b8f565b9050613617565b506137666137b39183612c93565b7ffcfc5aad0000000000000000000000000000000000000000000000000000000083527fffffffff0000000000000000000000000000000000000000000000000000000016600452602490565b90fd5b919290926004841061391d576137cc8484612c93565b7f8dd7712f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008216146138d7575b8061384384847fffffffff0000000000000000000000000000000000000000000000000000000094614fb4565b167fb61d27f600000000000000000000000000000000000000000000000000000000810361388f575050506004013573ffffffffffffffffffffffffffffffffffffffff16905061368b565b919290917f34fcd5be00000000000000000000000000000000000000000000000000000000146138c0575b50505050565b836138ce9461370e92612b9d565b388080806138ba565b5092806138e49293612b9d565b929091907fffffffff000000000000000000000000000000000000000000000000000000006139166107938686612b8f565b9050613816565b61397861392a8585612c93565b7ffcfc5aad000000000000000000000000000000000000000000000000000000006000527fffffffff0000000000000000000000000000000000000000000000000000000016600452602490565b6000fd5b9081156139b9575060a4601f19601f604060e48501519463ed6dfb1360448201523360848201523485820152608060c48201520193011601815290565b611537915060006132a5565b91908215613a0457505060a4601f19601f604060e48501519463ed6dfb1360448201523360848201523485820152608060c48201520193011601815290565b61153792506132a5565b91908215613a2657505060e46101b3910134306134fe565b6101b392503430612f6b565b613ad490613ac6613a96949360006040519687947f465d33e000000000000000000000000000000000000000000000000000000000602087015282602487015282604487015233606487015234608487015260c060a487015260e48601908361321d565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc8584030160c486015261321d565b03601f198101845283612322565b633d6bda32602483015260a060a483015260c482018051601f19019052565b9392613ad492613ac691613a966040519788957f465d33e0000000000000000000000000000000000000000000000000000000006020880152600060248801526000604488015233606488015234608488015260c060a488015260e487019161321d565b909150600063ffffffff8360401c169260601c9183604482015260c481015190826044838301015260406024389301910183855af115613b95575050565b613cd9565b92600091936040602463ffffffff87831c169660601c958760448501528460c48501519182860182806044830152613bea575b505050601f19601f389601160101910183855af115613b95575050565b6064908982601f198616830101520137843880613bcd565b600101600092613c1c826001600052602052604060002090565b5490604051945b600183161580613cd0575b15613c5a576001613c53910192808460051b8801528490600052602052604060002090565b5491613c23565b9250939290506002613c7a613c74835460ff9060b01c1690565b60ff1690565b91019060019160005b828110613c9f57505050506001810160051b8201604052815290565b90919293613cbc6001809201958490600052602052604060002090565b5491828660051b8801520192919092613c83565b50821515613c2e565b604051916018830152601482015263a32d2f5d81526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b6040519160188301526014820152634622c74881526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b6040519160188301526014820152635f85b3b481526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b90604051926018840152601483015281526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b9291633f41826e613297613e1b6080946040519283917fe7db7f7e000000000000000000000000000000000000000000000000000000006020840152602483019960008b5260006044850152336064850152608484015260a483019660a0885260c484019161321d565b945252565b9160ff613e2d8385615301565b3560f81c911690818110613ee95714613e495736926000929190565b91826005116101a3576005600183013560e01c0163ffffffff81116105145763ffffffff1692836005116101a3578084116101a357613ead907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8501948185612c7b565b9190928415613ebf5760050193929190565b7fb91b669d0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f95c6cb380000000000000000000000000000000000000000000000000000000060005260046000fd5b6000906040601f19601f602095978063ffffffff8a861c169960601c988a6044880152818060c4890152613f8e575b5050011660a4019101845afa15613f57575050565b60405191601883015260148201526346cfe46d81526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b8760e48684168901015260e48701378038613f42565b91909182156122725760ff813560f81c03613fea57826001116101a357600101917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f151d90fe0000000000000000000000000000000000000000000000000000000060005260046000fd5b929092600261406d6140668674ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b809661530a565b945460a01c161561418457601f93929160209182601f1960009784899563ffffffff8a60401c169960601c983060248801528a604488015260a060a4880152818060c489015261416e575b5050011660c4019101845afa601f3d111615614169575050518060201b6101a3577fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e0000000000000000000000000000000000000000000000000000000014614145577fffffffff0000000000000000000000000000000000000000000000000000000090565b7f1626ba7e0000000000000000000000000000000000000000000000000000000090565b613d47565b8760e48684168901015260e487013780386140b8565b7fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000847f07391694000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b919091357fffffffffffffffffffffffffffffffffffffffffff000000000000000000000081169260158110614208575050565b7fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000929350829060150360031b1b161690565b919091357fffffffffffff00000000000000000000000000000000000000000000000000008116926006811061426e575050565b7fffffffffffff0000000000000000000000000000000000000000000000000000929350829060060360031b1b161690565b939290916142dd6142c36142bd6142b78487612c21565b906141d4565b60581c90565b74ffffffffffffffffffffffffffffffffffffffffff1690565b9074ffffffffffffffffffffffffffffffffffffffff0482166143408174ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b9260ff614352855460ff9060a81c1690565b166144045782916143638388612c2f565b61436c9161423a565b60d01c988961437c85808b612c40565b9161438693615389565b948161439385808b612c40565b9190946143a290600116151590565b6000149a61141b996143e7996143ce6143e1986115379f986113e8976143d39a6107a2576000926137b6565b615440565b60009080516143ee5761332a565b94612c40565b9030612f53565b905061208e6143fe84808a612c40565b906132a5565b7fd7c794140000000000000000000000000000000000000000000000000000000060005260046000fd5b9291909361448b61448261046d8474ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b9560009561551a565b928551805b6144bf57506144aa9392916144a491613fa4565b9161576b565b9151156144bb5790611537916145ba565b5090565b61451e9295917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6145029201966104c9613c746144fd8a8c51612b19565b612aec565b949093916145166118bb6104e38b8d6123a6565b9384896156a9565b9173ffffffffffffffffffffffffffffffffffffffff83169060018211614552575050869161454c91615742565b95614490565b7fc616f69a000000000000000000000000000000000000000000000000000000006000527fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660045273ffffffffffffffffffffffffffffffffffffffff1660245260446000fd5b90600173ffffffffffffffffffffffffffffffffffffffff6145dc8385615837565b9316036145ea575060011790565b73ffffffffffffffffffffffffffffffffffffffff161790565b74ffffffffffffffffffffffffffffffffffffffffff90604081901c63ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000018101613583575060581c74ffffffffffffffffffffffffffffffffffffffff00166004171690565b91908110156122725760051b0190565b3561153781610299565b919273ffffffffffffffffffffffffffffffffffffffff6146be845473ffffffffffffffffffffffffffffffffffffffff1690565b7fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000841660181a91606085901c91168061488c575084547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90911617845561477c905b84547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1660a09190911b74ff000000000000000000000000000000000000000016178455565b60005b81811061478d575050505050565b6147a061479b82848861466f565b61467f565b6147da610be76147d17fffffffff000000000000000000000000000000000000000000000000000000008416610bb9565b60038801614b4a565b6147e7575060010161477f565b613978906148176118bb7fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000871681565b7fd5d4419e000000000000000000000000000000000000000000000000000000006000527fffffffff000000000000000000000000000000000000000000000000000000009091166004527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016602452604490565b9073ffffffffffffffffffffffffffffffffffffffff16036148b15761477c90614736565b7f50a67a840000000000000000000000000000000000000000000000000000000060005260046000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000061490991168092614b4a565b156149115750565b7f236e22ad0000000000000000000000000000000000000000000000000000000060005260045260246000fd5b91602061153793818152019161321d565b60409073ffffffffffffffffffffffffffffffffffffffff61153794931681528160208201520190610d1d565b9291908161498b575b50509050565b6149b8610be77fb02cc19200000000000000000000000000000000000000000000000000000000866158ea565b614a905773ffffffffffffffffffffffffffffffffffffffff841691823b156101a357614a1892600092836040518096819582947f6d61fe700000000000000000000000000000000000000000000000000000000084526004840161493e565b03925af19081614a75575b50614a6b5750614a31612f80565b90614a676040519283927f1672bd930000000000000000000000000000000000000000000000000000000084526004840161494f565b0390fd5b9050803880614985565b80614a846000614a8a93612322565b806110d7565b38614a23565b7f70560bfc0000000000000000000000000000000000000000000000000000000060005273ffffffffffffffffffffffffffffffffffffffff841660045260246000fd5b92919081614ae25750509050565b6149b8610be77f28171ad000000000000000000000000000000000000000000000000000000000866158ea565b92919081614b1d5750509050565b6149b8610be77fab3e34c100000000000000000000000000000000000000000000000000000000866158ea565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169081158015614c0a575b614c035760016000528060205260406000205480158015614bf9575b15614bd157508181614bcc93614bb6614bc6946001600052602052604060002090565b5590600052602052604060002090565b60019055565b600190565b9180614be7836001600052602052604060002090565b55600052602052604060002055600190565b5060018116614b93565b5050600090565b5081600052806020526040600020541515614b77565b906000600160005282602052604060002054604051915b600182161580614c79575b15614c665760010190808260051b8401526000528360205260406000205490614c37565b6001810160051b83016040528252509150565b50811515614c42565b80518060011c9060005b828110614c995750505050565b80820390828211610514577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116105145781614cdb600193876123a6565b5190614cf2614cea84896123a6565b5191886123a6565b52614cfd82876123a6565b5201614c8c565b614d0d81612345565b91614d1b6040519384612322565b818352601f19614d2a83612345565b01366020850137600060015b838210614d44575050505090565b600052816020526001604060002054917fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000008316614d8182886123a6565b520190614d36565b916020928160808263ffffffff601f19601f850116968060601c835260401c16878201526040808201528260608201520192614dcc575b50509060800181520190565b90816040608094600086601f19899716860101523e01013880614dc0565b91906040600063ffffffff83831c169260601c9483602482015260208151910182875af1603f3d11166020600051141692602051937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc13d0185101615614e4e575050565b60405191601883015260148201526374a1a72c81526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b600193926000929181614e985750505050565b9394929373ffffffffffffffffffffffffffffffffffffffff1690813b15614f3557918491614efe93836040518096819582947f8a91b0e3000000000000000000000000000000000000000000000000000000008452602060048501818152019161321d565b03925af19081614f21575b50614f19575090388080806138ba565b9190506138ce565b83614f2e91949294612322565b9138614f09565b8480fd5b600191825b15614f5e575b600092835260208290526040832080549084905592614f3e565b600183161580614f72575b614f4457915050565b50821515614f69565b60031115614f8557565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91614fbe81614f7b565b806150275750610be7614fd1918361597d565b614fd85750565b7fcf7b49f6000000000000000000000000000000000000000000000000000000006000527fffffffff000000000000000000000000000000000000000000000000000000001660045260246000fd5b80615033600192614f7b565b0361504557610be7614fd19183615a5a565b615052610be7828461597d565b9081615061575b50614fd85750565b6150709150610be79083615a5a565b38615059565b9192908201916020818403126101a35780359067ffffffffffffffff82116101a357019180601f840112156101a357823567ffffffffffffffff81116101a3578160208260051b860101116101a357928082037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019060005b8581106150ff5750505050505050565b60208160051b8301013567ffffffffffffffff81116101a357838112156101a3578201602081013573ffffffffffffffffffffffffffffffffffffffff81169081036101a3573014615155575b506001016150ef565b60608101359067ffffffffffffffff82116101a3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1818703018212156101a3570160208101359063ffffffff82116101a3576040019080860382136101a3576004136101a35735908160e01c63b61d27f6811490811561520b575b506136a95761520586897fffffffff0000000000000000000000000000000000000000000000000000000060019516614fb4565b9061514c565b6334fcd5be915014386151d1565b6152709093929361526a8174ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b9061530a565b9263ffffffff8460401c169360601c9230602484015284604484015260c060a48401528060c4840192835193602085019052838501828060448301526152e8575b505050833b156101a3576152db9260206044600094601f19601f389601160101910183865af11590565b6152e3575050565b613d10565b606490600082601f1986168301015201378038806152b1565b90156122725790565b90600482161561534b575060581b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffff00000000000000001790565b5460601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660389190911b6bffffffff0000000000000000161790565b65ffffffffffff60809392604295806040519586378420927f9b23e06584efc6b65fc854cee55011d89f86485487b6db36aed7d23884711ea38552602085015216604083015260608201522060606040517f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a7946921881524660208201523060408201522090604051917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522090565b91817f1626ba7e000000000000000000000000000000000000000000000000000000009461549383615498957fffffffff0000000000000000000000000000000000000000000000000000000097613db1565b614014565b16036154a057565b7f01334f770000000000000000000000000000000000000000000000000000000060005260046000fd5b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112156101a357016020813591019167ffffffffffffffff82116101a35781360383136101a357565b61153790615695926040519384927f2a3d428c00000000000000000000000000000000000000000000000000000000602085015260006024850152606060448501526155866084850161556c836101a8565b73ffffffffffffffffffffffffffffffffffffffff169052565b602081013560a48501526156646156586155f96155bc6155a960408601866154ca565b61012060c48b01526101a48a019161321d565b6155c960608601866154ca565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c8a84030160e48b015261321d565b608084013561010488015260a084013561012488015260c084013561014488015261562760e08501856154ca565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c898403016101648a015261321d565b916101008101906154ca565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c8684030161018487015261321d565b90606483015203601f198101835282612322565b90929160006084602092601f19601f63ffffffff8960401c169860601c978960248901528061018489015194858a0182808983015261572c575b5050500116010182840182865af1601f3d1116156157045750505060005190565b6004015163ffffffff16632a3d428c0361572257634db96e31613d7e565b6319aed90d613d7e565b60a49089828886168301015201378038806156e3565b73ffffffffffffffffffffffffffffffffffffffff806157628484615837565b93169116171790565b939260016157c36157bc8774ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b809761530a565b955460a01c16156157e7579384611537949551630ab8785f600483015281526156a9565b7fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000857f566f60e2000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b9061584a8260a01c65ffffffffffff1690565b65ffffffffffff8116156158dd575b65ffffffffffff6158ae61587b6158a761587b849561588661587b8960a01c90565b65ffffffffffff1690565b868116156158d6575b8616908616818111156158ca575060a01b9760d01c90565b9460d01c90565b169116818110156158c1575060d01b1790565b60d01b90501790565b60a01b90509760d01c90565b508561588f565b5065ffffffffffff615859565b6000906020926040517fffffffff00000000000000000000000000000000000000000000000000000000858201927f01ffc9a700000000000000000000000000000000000000000000000000000000845216602482015260248152615950604482612322565b5191617530fa6000513d82615971575b508161596a575090565b9050151590565b60201115915038615960565b9061598a8260e01c6130ec565b9182156159f4575b508161599c575090565b600491506159ea9074ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b5460a01c16151590565b60ff919250615a4e907fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b5460a81c169038615992565b906003615ac87fffffffff000000000000000000000000000000000000000000000000000000009274ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b01911660005260205260406000205415159056fea164736f6c634300081a000a0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0320000000000000000000000000000000000008e6a39e03c7156e46b238c9e2036
Deployed Bytecode
0x60806040526004361015610026575b36156100245761001c61252b565b602081519101f35b005b60003560e01c80621a63e91461014957806301ffc9a714610180578063150b7a021461017b5780631626ba7e1461017657806319822f7c146101715780631bbf564c1461016c57806334fcd5be146101675780634f1ef2861461016257806352d1902d1461015d5780635998db5c14610158578063757c8a26146101535780638dd7712f1461014e57806393b1dc61146101495780639cfd7cff14610144578063b0d691fe1461013f578063b61d27f61461013a578063b6b1ccfe14610135578063bc197c8114610130578063d31b575b1461012b578063e919a62314610126578063f23a6e61146101215763f2680c0f0361000e57611e64565b611dd3565b611b7b565b611ab7565b61193a565b61164b565b61153a565b6114b7565b61143a565b61027f565b611318565b611280565b611162565b6110e2565b610f5f565b610ddc565b610973565b610579565b6103a3565b610312565b6102c3565b73ffffffffffffffffffffffffffffffffffffffff8116036101a357565b600080fd5b35906101b382610185565b565b9181601f840112156101a35782359167ffffffffffffffff83116101a357602083818601950101116101a357565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101a35760043561021981610185565b9160243567ffffffffffffffff81116101a35760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82850301126101a357600401916044359067ffffffffffffffff82116101a35761027b916004016101b5565b9091565b346101a357610024610290366101e3565b9291909161209f565b7fffffffff000000000000000000000000000000000000000000000000000000008116036101a357565b346101a35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357602061030860043561030381610299565b6120f5565b6040519015158152f35b346101a35760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35761034c600435610185565b610357602435610185565b60643567ffffffffffffffff81116101a3576103779036906004016101b5565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b346101a35760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760243560043567ffffffffffffffff82116101a35761041461040e74ffffffffffffffffffffffffffffffffffffffff049336906004016101b5565b90612a20565b92919390931692610478838261047261046d8874ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b6135a3565b94613db1565b9082515b808015610519577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0191845191820391600183019081116105145782116105145761050f956104cf9260ff165b91613e20565b939296919093966105096104e385896123a6565b517fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001690565b86613f13565b61047c565b612abd565b610566610533888661052b8a88613fa4565b929091614014565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681529081906020820190565b0390f35b90816101209103126101a35790565b346101a35760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043567ffffffffffffffff81116101a3576105cb61064a91369060040161056a565b602435604435916105da612b26565b60009060208101356105eb816131d8565b6105f9610100840184612281565b81929192819461060b84600216151590565b6107aa575b50505061065760608501916106258387612281565b74ffffffffffffffffffffffffffffffffffffffff04839293169b8c92600116151590565b156107a2576000926137b6565b60ff6106b06106a68a74ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b5460b01c60ff1690565b1615159081610739575b5061070f576105669686956106ce9461442e565b90801561070857906106df916145ba565b915b6106f7575b506040519081529081906020820190565b60009081803892335af150386106e6565b50916106e1565b7f5f49f00a0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f8dd7712f00000000000000000000000000000000000000000000000000000000915061079961079361078d7fffffffff000000000000000000000000000000000000000000000000000000009388612281565b90612b8f565b90612c93565b161415386106ba565b6001926137b6565b65ffffffffffff97508293955061087c94506108769061084c6108706107ea6107e16107db61079389899a99612b8f565b60e01c90565b63ffffffff1690565b9a8b9361086961086388876108578c6108346107e16107db61079361081861081188612cf9565b8987612b9d565b98909761082d61082782612cf9565b91612d07565b9187612c7b565b9a8d6108518d61084c61084685612d07565b94612d07565b612d15565b92612c7b565b929091604051966142a0565b91604052565b1660a01b90565b99612d07565b91612c7b565b91388080610610565b9181601f840112156101a35782359167ffffffffffffffff83116101a3576020808501948460051b0101116101a357565b9060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101a3576004357fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000811681036101a3579160243567ffffffffffffffff81116101a3578161092d91600401610885565b9290929160443567ffffffffffffffff81116101a35781610950916004016101b5565b929092916064359067ffffffffffffffff82116101a35761027b91600401610885565b346101a357610981366108b6565b90919293946109ec61099161261c565b966109e461099e8a614604565b74ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b928984614689565b60005b828110610ab257610024878773ffffffffffffffffffffffffffffffffffffffff63ffffffff610a858d610a798c610a72610a6c7fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000085165b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690565b60601c90565b9687614b0f565b60401c63ffffffff1690565b1691167fc36a28045e90a1163d24d4216c8cfd8c44c4e835a486fb84d511d6b9e0736db9600080a361295f565b610af7610ad2610acc610ac6848789612d22565b90612bd8565b90612d39565b7fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001690565b90610b0d610b06828688612d22565b8091612be6565b9092670100000000000000811615610c8057845460a81c60ff169060ff60018184160111610c5657610b8b91610ad29060010160ff165b87547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1660a89190911b75ff00000000000000000000000000000000000000000016178755565b90610beb610be7610bde7fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000085165b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690565b60018801614b4a565b1590565b610c2c57600193610c21610a6c610c26947fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690565b614ad4565b016109ef565b7f298ac3da0000000000000000000000000000000000000000000000000000000060005260046000fd5b7fe330dd260000000000000000000000000000000000000000000000000000000060005260046000fd5b90610c90855460ff9060b01c1690565b9360ff60018187160111610c5657610d13610a6c610a47610d1895610d06610cbe60019a600160ff91011690565b8b547fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff1660b09190911b76ff0000000000000000000000000000000000000000000016178b55565b610ad28160028c016148db565b61497c565b610c26565b919082519283825260005b848110610d49575050601f19601f8460006020809697860101520116010190565b80602080928401015182828601015201610d28565b602081016020825282518091526040820191602060408360051b8301019401926000915b838310610d9157505050505090565b9091929394602080610dcd837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc086600196030187528951610d1d565b97019301930191939290610d82565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043567ffffffffffffffff81116101a357610e26903690600401610885565b610e2e61261c565b6060337f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da03273ffffffffffffffffffffffffffffffffffffffff1614610f0b5750610e778261235d565b9160005b818110610e9e5750506105669250610e929061295f565b60405191829182610d5e565b80610ee7610eb7610eb2600194868a612232565b612277565b6020610ec484878b612232565b013590610edf610ed585888c612232565b6040810190612281565b929091612f6b565b610eef612f80565b610ef982876123a6565b52610f0481866123a6565b5001610e7b565b91909260005b828110610f2757505050610e926105669261295f565b80610f59610f3b610eb26001948787612232565b6020610f48848888612232565b013590610edf610ed5858989612232565b01610f11565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357600435610f9581610185565b60243567ffffffffffffffff81116101a357610fb59036906004016101b5565b919091307f00000000000000000000000000000000000002377b26b1eda7b0bc371c60dd4f146110c957610fe761261c565b9073ffffffffffffffffffffffffffffffffffffffff60009316906352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602060016004601d865afa51036110bb57817fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8580a280827f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55611097575b836110948461295f565b80f35b908184926040519687378538925af4156110b257828061108a565b503d90823e3d90fd5b6355299b496001526004601dfd5b639f03a0266000526004601cfd5b60009103126101a357565b346101a35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357307f00000000000000000000000000000000000002377b26b1eda7b0bc371c60dd4f036110c95760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043560243567ffffffffffffffff81116101a3576111af9036906004016101b5565b906044359283151584036101a3576064356111c861261c565b94846040519485376001146112215750f05b8015611213576111ec6105669261295f565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b637e16b8cd6000526004601cfd5b929190f56111da565b906020808351928381520192019060005b8181106112485750505090565b82517fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001684526020938401939092019160010161123b565b346101a35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760606112c56004356112c081610299565b6123ba565b6105666040519283926020845273ffffffffffffffffffffffffffffffffffffffff81511660208501526020810151151560408501526040810151151582850152015160808084015260a083019061122a565b346101a35760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043567ffffffffffffffff81116101a35761136a61002491369060040161056a565b611372612b26565b61141b6114156113ed6113e874ffffffffffffffffffffffffffffffffffffffff046113a160208701356131d8565b1674ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b613208565b6000908051611420575b6114048261140f9261332a565b946060810190612281565b91613440565b306134e8565b61295f565b905061140f6114046114313661323e565b929150506113f7565b346101a35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357610566604080519061147b8183612322565b601d82527f616c6368656d792e6d6f64756c61722d6163636f756e742e322e302e30000000602083015251918291602083526020830190610d1d565b346101a35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032168152f35b906020611537928181520190610d1d565b90565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043561157081610185565b6044359060243567ffffffffffffffff83116101a3576115ac61159a6105669436906004016101b5565b906115a361261c565b93606095612f6b565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032163303611607575b6115f39061295f565b604051918291602083526020830190610d1d565b90506115f3611614612f80565b9190506115ea565b600435907fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000821682036101a357565b346101a35760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35761168261161c565b60243567ffffffffffffffff81116101a3576116a29036906004016101b5565b9060443567ffffffffffffffff81116101a3576116c3903690600401610885565b90926116cd61261c565b936116da61099e87613514565b90600193806117e9575b50506100249561173d826116fd60016117529501614f39565b61170960028201614f39565b61171560038201614f39565b80547fffffffffffffffffff0000000000000000000000000000000000000000000000169055565b606081901c9160409190911c63ffffffff1690565b919093836117ae575b5050604051911515825263ffffffff169173ffffffffffffffffffffffffffffffffffffffff16907f43f7309d11ba6b2e180e9ab8a6da09d2fa1f585d7daed4b26c1c7f2a90b867a290602090a361295f565b7f43f7309d11ba6b2e180e9ab8a6da09d2fa1f585d7daed4b26c1c7f2a90b867a292935063ffffffff916117e29186614e85565b929161175b565b6117f283613208565b6117fb846135a3565b916118098351835190612d15565b810361191057919290600093805185915b81831061188f575050508051926000925b84841061183b57505050506116e4565b9091929394976118726001916118528b8686612d22565b90611863610a6c6104e38b8b6123a6565b8361187d575b505050996132fd565b95940192919061182b565b6118879350614e85565b388080611869565b909194929395986118f06001916118a78c878b612d22565b906118e061173d6118bb6104e38d8a6123a6565b7fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690565b50836118fe575b5050509a6132fd565b95019190959392949561181a565b6119089350614e85565b3880806118e7565b7fa24a13a60000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a35760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357611974600435610185565b61197f602435610185565b60443567ffffffffffffffff81116101a35761199f903690600401610885565b505060643567ffffffffffffffff81116101a3576119c1903690600401610885565b505060843567ffffffffffffffff81116101a3576119e39036906004016101b5565b50506040517fbc197c81000000000000000000000000000000000000000000000000000000008152602090f35b906020825260ff81511660208301526060611a52611a3d60208401516080604087015260a086019061122a565b6040840151601f19868303018487015261122a565b910151916080601f19828403019101526020808351928381520192019060005b818110611a7f5750505090565b82517fffffffff0000000000000000000000000000000000000000000000000000000016845260209384019390920191600101611a72565b346101a35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357610566611af161161c565b60405190611afe82612301565b60008252611b64600360208401606081526040850160608152611b4a611b2e61099e606089019760608952613514565b9260ff845460a01c168852611b42846135a3565b809152614c82565b611b5382613208565b90611b5d82614c82565b5201614c20565b90611b6e82614c82565b5260405191829182611a10565b346101a357611b89366108b6565b7f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0054969590949193919067ffffffffffffffff611bd660ff60408b901c16159967ffffffffffffffff1690565b1680159081611dcb575b6001149081611dc1575b159081611db8575b50611d8e57611c739688611c6a60017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000007f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd005416177f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0055565b611d1357612d9f565b611c7957005b611ce47fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0054167f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0055565b604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b611d89680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd005416177f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd0055565b612d9f565b7ff92ee8a90000000000000000000000000000000000000000000000000000000060005260046000fd5b90501538611bf2565b303b159150611bea565b899150611be0565b346101a35760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a357611e0d600435610185565b611e18602435610185565b60843567ffffffffffffffff81116101a357611e389036906004016101b5565b505060206040517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a35760043567ffffffffffffffff81116101a357611eae9036906004016101b5565b60243567ffffffffffffffff81116101a35761040e611ed19136906004016101b5565b74ffffffffffffffffffffffffffffffffffffffff04839592931694611f43611f3a8774ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b91600116151590565b156120945790611f5760005b8787866137b6565b8094611fb38592611fab61046d8a74ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b968387613af3565b9385515b808015612029577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01938751918203916001830190811161051457821161051457612022986120089260ff166104c9565b9692999190969961201c6104e3888c6123a6565b89613b9a565b9392611fb7565b505061205e61205086956113e8610566988c6120488d6120649a613fa4565b929091615219565b60009080516120815761332a565b93613a0e565b61207561206f612f80565b9161295f565b60405191829182611526565b905061208e8484886139c5565b9061332a565b90611f576001611f4f565b505050506120ab61261c565b600060405136828237389036907f0000000000000000000000000000000000008e6a39e03c7156e46b238c9e20365af4156120e9576101b39061295f565b6040513d6000823e3d90fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167fffffffff0000000000000000000000000000000000000000000000000000000081146121fd577f150b7a0200000000000000000000000000000000000000000000000000000000811480156121d4575b80156121ab575b6121a5576000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd03602052604060002054151590565b50600190565b507f01ffc9a700000000000000000000000000000000000000000000000000000000811461216f565b507f4e2312e0000000000000000000000000000000000000000000000000000000008114612168565b50600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b91908110156122725760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1813603018212156101a3570190565b612203565b3561153781610185565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101a3570180359067ffffffffffffffff82116101a3576020019181360383136101a357565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff82111761231d57604052565b6122d2565b90601f601f19910116810190811067ffffffffffffffff82111761231d57604052565b67ffffffffffffffff811161231d5760051b60200190565b9061236782612345565b6123746040519182612322565b828152601f196123848294612345565b019060005b82811061239557505050565b806060602080938501015201612389565b80518210156122725760209160051b010190565b90604051916123c883612301565b60008352600060208401526000604084015260608084015282612436827fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b9160e01c61244381612f9e565b1561249a57610be78161246e61245b612473946130ec565b3086528015602087015215156040860152565b61312c565b61249557506001612485915b01614c20565b61248e81614c82565b6060830152565b925050565b508161250a600192604061250361248596546124e66124cc8273ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff168552565b6124fa60a082901c60ff1615156020860152565b60a81c60ff1690565b1515910152565b61247f565b67ffffffffffffffff811161231d57601f01601f191660200190565b7fffffffff000000000000000000000000000000000000000000000000000000006000351673ffffffffffffffffffffffffffffffffffffffff6125ba827fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b54169081156125ef57506125e46125cf61261c565b916040519036825236600060208401376134e8565b61153761206f612f80565b7ffcfc5aad0000000000000000000000000000000000000000000000000000000060005260045260246000fd5b60009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0321633141580612955575b806128c6575b1561283b573360081b74ffffffffffffffffffffffffffffffffffffffff00166004179161269383366135b8565b6126e061046d8474ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b92835180612829575b805b6127e757506115379293506127c3906127bd6127767fffffffff00000000000000000000000000000000000000000000000000000000600035167fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b9174ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b90613c02565b60009181516127d3575b5061332a565b6127e0919250369061397c565b90386127cd565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0193846128223661281c6104e384866123a6565b86613b57565b90946126eb565b91506128353636613a32565b916126e9565b90611537906128c16128bc7fffffffff00000000000000000000000000000000000000000000000000000000600035167fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b6131cc565b6127c3565b50612950610be76129467fffffffff00000000000000000000000000000000000000000000000000000000600035167fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b5460a01c60ff1690565b612665565b503033141561265f565b601f19815191019060005b81811061297657505050565b8251809303928351601f196000602087015193635d413a8188527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe4389101601c890183865af1950194156129ce57505060010161296a565b604051916018830152601482015263f19fc59381526040808201523d60608201523d612a06575b601c601f19601f3d01166064019101fd5b60006080601f193d16830101523d6000608083013e6129f5565b91600582106101a35782358060f81c9060048216600014612a8857601584106101a35760501c74ffffffffffffffffffffffffffffffffffffffff00161792601501917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb0190565b60d01c64ffffffff00161792600501917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161051457565b9190820391821161051457565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032163303612b6557565b7fd663742a0000000000000000000000000000000000000000000000000000000060005260046000fd5b906004116101a35790600490565b90929192836004116101a35783116101a357600401917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0190565b906019116101a35790601990565b90929192836019116101a35783116101a357601901917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe70190565b906015116101a35790601590565b90601b116101a35760150190600690565b9092919283601b116101a35783116101a357601b01917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe50190565b909392938483116101a35784116101a3578101920390565b919091357fffffffff0000000000000000000000000000000000000000000000000000000081169260048110612cc7575050565b7fffffffff00000000000000000000000000000000000000000000000000000000929350829060040360031b1b161690565b600401908160041161051457565b600801908160081161051457565b9190820180921161051457565b908210156122725761027b9160051b810190612281565b919091357fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000081169260198110612d6d575050565b7fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000929350829060190360031b1b161690565b94929591612dbb90612db361099e88614604565b928784614689565b60005b828110612e445750505050612e1a82610a7963ffffffff93610a72610a6c610a4773ffffffffffffffffffffffffffffffffffffffff987fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001690565b1691167fc36a28045e90a1163d24d4216c8cfd8c44c4e835a486fb84d511d6b9e0736db9600080a3565b612e58610ad2610acc610ac6848789612d22565b90612e67610b06828688612d22565b9092670100000000000000811615612f1057845460a81c60ff169060ff60018184160111610c5657612ea291610ad29060010160ff16610b44565b90612ed4610be7610bde7fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000008516610bb9565b610c2c57600193610c21610a6c612f0a947fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690565b01612dbe565b90612f20855460ff9060b01c1690565b9360ff60018187160111610c5657610d13610a6c610a47612f4e95610d06610cbe60019a600160ff91011690565b612f0a565b6000906101b3936040519381855260208501376134fe565b6101b3936040519381855260208501376134fe565b604051903d8252601f19603f3d840101166040523d6000602084013e565b612fa7816130ec565b9081156130d8575b81156130c4575b81156130b0575b811561309c575b8115613088575b8115613074575b8115613060575b811561304c575b8115613038575b8115613024575b8115613010575b8115612fff575090565b63e919a623915063ffffffff161490565b63ffffffff81166352d1902d149150612ff5565b63ffffffff811663d31b575b149150612fee565b63ffffffff811663757c8a26149150612fe7565b63ffffffff8116639cfd7cff149150612fe0565b63ffffffff811663150b7a02149150612fd9565b63ffffffff81166301ffc9a7149150612fd2565b63ffffffff8116631626ba7e149150612fcb565b63ffffffff811663f23a6e61149150612fc4565b63ffffffff811663bc197c81149150612fbd565b63ffffffff81166319822f7c149150612fb6565b63ffffffff811663b0d691fe149150612faf565b6130f58161312c565b908115613118575b8115613107575090565b63f2680c0f915063ffffffff161490565b63ffffffff8116638dd7712f1491506130fd565b63ffffffff1663b61d27f681149081156131be575b81156131b1575b81156131a3575b8115613195575b8115613187575b8115613179575b811561316e575090565b634f1ef28691501490565b635998db5c81149150613164565b63b6b1ccfe8114915061315d565b6393b1dc6181149150613156565b631bbf564c8114915061314f565b621a63e981149150613148565b6334fcd5be81149150613141565b60016115379101614c20565b60401c60048116156131fe5774ffffffffffffffffffffffffffffffffffffffffff1690565b64ffffffffff1690565b8054611537916002019060b01c60ff16614d04565b601f8260209493601f19938186528686013760008582860101520116010190565b611537613297916040519283917fed6dfb13000000000000000000000000000000000000000000000000000000006020840152600060248401523360448401523460648401526080608484015260a4830190600061321d565b03601f198101835282612322565b90611537906132976040519384927fed6dfb13000000000000000000000000000000000000000000000000000000006020850152600060248501523360448501523460648501526080608485015260a484019161321d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146105145760010190565b805160405193926000929091805b61334a57505050825260208201604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161337681836123a6565b517fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000081169067040000000000000016156133ea576133b48185614dea565b9067020000000000000081166133cd575b505080613338565b6133e3916133dd91849799614d89565b966132fd565b93386133c5565b6702000000000000008116613401575b5080613338565b6133dd61343a9183969860809063ffffffff6020938060601c835260401c16838201526040808201526000606082015201608081520190565b936133fa565b918215613482577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01908180845261347757505090565b600401602083013790565b8092506004116101a3577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201906134b98261250f565b926134c76040519485612322565b82845236818301116101a3576000926004601c930160208601378301015290565b600091389183602083519301915af1156120e957565b916000923892602083519301915af1156120e957565b74ffffffffffffffffffffffffffffffffffffffffff90606081901c9060401c63ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000018101613583575060081b74ffffffffffffffffffffffffffffffffffffffff00166004171690565b74ffffffffffffffffffffffffffffffffffffffff00915060081b161690565b8054611537916001019060a81c60ff16614d04565b60009160048210613758576135cd8284612c93565b7f8dd7712f000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821614613714575b806136456002847fffffffff0000000000000000000000000000000000000000000000000000000094614fb4565b167fb61d27f60000000000000000000000000000000000000000000000000000000081036136d3575050506004013573ffffffffffffffffffffffffffffffffffffffff165b73ffffffffffffffffffffffffffffffffffffffff1630146136a957565b7f54ff929d0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f34fcd5be00000000000000000000000000000000000000000000000000000000146136fe57505050565b61370e826002936101b395612b9d565b90615076565b5091908061372192612b9d565b90917fffffffff000000000000000000000000000000000000000000000000000000006137516107938486612b8f565b9050613617565b506137666137b39183612c93565b7ffcfc5aad0000000000000000000000000000000000000000000000000000000083527fffffffff0000000000000000000000000000000000000000000000000000000016600452602490565b90fd5b919290926004841061391d576137cc8484612c93565b7f8dd7712f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008216146138d7575b8061384384847fffffffff0000000000000000000000000000000000000000000000000000000094614fb4565b167fb61d27f600000000000000000000000000000000000000000000000000000000810361388f575050506004013573ffffffffffffffffffffffffffffffffffffffff16905061368b565b919290917f34fcd5be00000000000000000000000000000000000000000000000000000000146138c0575b50505050565b836138ce9461370e92612b9d565b388080806138ba565b5092806138e49293612b9d565b929091907fffffffff000000000000000000000000000000000000000000000000000000006139166107938686612b8f565b9050613816565b61397861392a8585612c93565b7ffcfc5aad000000000000000000000000000000000000000000000000000000006000527fffffffff0000000000000000000000000000000000000000000000000000000016600452602490565b6000fd5b9081156139b9575060a4601f19601f604060e48501519463ed6dfb1360448201523360848201523485820152608060c48201520193011601815290565b611537915060006132a5565b91908215613a0457505060a4601f19601f604060e48501519463ed6dfb1360448201523360848201523485820152608060c48201520193011601815290565b61153792506132a5565b91908215613a2657505060e46101b3910134306134fe565b6101b392503430612f6b565b613ad490613ac6613a96949360006040519687947f465d33e000000000000000000000000000000000000000000000000000000000602087015282602487015282604487015233606487015234608487015260c060a487015260e48601908361321d565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc8584030160c486015261321d565b03601f198101845283612322565b633d6bda32602483015260a060a483015260c482018051601f19019052565b9392613ad492613ac691613a966040519788957f465d33e0000000000000000000000000000000000000000000000000000000006020880152600060248801526000604488015233606488015234608488015260c060a488015260e487019161321d565b909150600063ffffffff8360401c169260601c9183604482015260c481015190826044838301015260406024389301910183855af115613b95575050565b613cd9565b92600091936040602463ffffffff87831c169660601c958760448501528460c48501519182860182806044830152613bea575b505050601f19601f389601160101910183855af115613b95575050565b6064908982601f198616830101520137843880613bcd565b600101600092613c1c826001600052602052604060002090565b5490604051945b600183161580613cd0575b15613c5a576001613c53910192808460051b8801528490600052602052604060002090565b5491613c23565b9250939290506002613c7a613c74835460ff9060b01c1690565b60ff1690565b91019060019160005b828110613c9f57505050506001810160051b8201604052815290565b90919293613cbc6001809201958490600052602052604060002090565b5491828660051b8801520192919092613c83565b50821515613c2e565b604051916018830152601482015263a32d2f5d81526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b6040519160188301526014820152634622c74881526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b6040519160188301526014820152635f85b3b481526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b90604051926018840152601483015281526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b9291633f41826e613297613e1b6080946040519283917fe7db7f7e000000000000000000000000000000000000000000000000000000006020840152602483019960008b5260006044850152336064850152608484015260a483019660a0885260c484019161321d565b945252565b9160ff613e2d8385615301565b3560f81c911690818110613ee95714613e495736926000929190565b91826005116101a3576005600183013560e01c0163ffffffff81116105145763ffffffff1692836005116101a3578084116101a357613ead907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8501948185612c7b565b9190928415613ebf5760050193929190565b7fb91b669d0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f95c6cb380000000000000000000000000000000000000000000000000000000060005260046000fd5b6000906040601f19601f602095978063ffffffff8a861c169960601c988a6044880152818060c4890152613f8e575b5050011660a4019101845afa15613f57575050565b60405191601883015260148201526346cfe46d81526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b8760e48684168901015260e48701378038613f42565b91909182156122725760ff813560f81c03613fea57826001116101a357600101917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f151d90fe0000000000000000000000000000000000000000000000000000000060005260046000fd5b929092600261406d6140668674ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b809661530a565b945460a01c161561418457601f93929160209182601f1960009784899563ffffffff8a60401c169960601c983060248801528a604488015260a060a4880152818060c489015261416e575b5050011660c4019101845afa601f3d111615614169575050518060201b6101a3577fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e0000000000000000000000000000000000000000000000000000000014614145577fffffffff0000000000000000000000000000000000000000000000000000000090565b7f1626ba7e0000000000000000000000000000000000000000000000000000000090565b613d47565b8760e48684168901015260e487013780386140b8565b7fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000847f07391694000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b919091357fffffffffffffffffffffffffffffffffffffffffff000000000000000000000081169260158110614208575050565b7fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000929350829060150360031b1b161690565b919091357fffffffffffff00000000000000000000000000000000000000000000000000008116926006811061426e575050565b7fffffffffffff0000000000000000000000000000000000000000000000000000929350829060060360031b1b161690565b939290916142dd6142c36142bd6142b78487612c21565b906141d4565b60581c90565b74ffffffffffffffffffffffffffffffffffffffffff1690565b9074ffffffffffffffffffffffffffffffffffffffff0482166143408174ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b9260ff614352855460ff9060a81c1690565b166144045782916143638388612c2f565b61436c9161423a565b60d01c988961437c85808b612c40565b9161438693615389565b948161439385808b612c40565b9190946143a290600116151590565b6000149a61141b996143e7996143ce6143e1986115379f986113e8976143d39a6107a2576000926137b6565b615440565b60009080516143ee5761332a565b94612c40565b9030612f53565b905061208e6143fe84808a612c40565b906132a5565b7fd7c794140000000000000000000000000000000000000000000000000000000060005260046000fd5b9291909361448b61448261046d8474ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b9560009561551a565b928551805b6144bf57506144aa9392916144a491613fa4565b9161576b565b9151156144bb5790611537916145ba565b5090565b61451e9295917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6145029201966104c9613c746144fd8a8c51612b19565b612aec565b949093916145166118bb6104e38b8d6123a6565b9384896156a9565b9173ffffffffffffffffffffffffffffffffffffffff83169060018211614552575050869161454c91615742565b95614490565b7fc616f69a000000000000000000000000000000000000000000000000000000006000527fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660045273ffffffffffffffffffffffffffffffffffffffff1660245260446000fd5b90600173ffffffffffffffffffffffffffffffffffffffff6145dc8385615837565b9316036145ea575060011790565b73ffffffffffffffffffffffffffffffffffffffff161790565b74ffffffffffffffffffffffffffffffffffffffffff90604081901c63ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000018101613583575060581c74ffffffffffffffffffffffffffffffffffffffff00166004171690565b91908110156122725760051b0190565b3561153781610299565b919273ffffffffffffffffffffffffffffffffffffffff6146be845473ffffffffffffffffffffffffffffffffffffffff1690565b7fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000841660181a91606085901c91168061488c575084547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90911617845561477c905b84547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1660a09190911b74ff000000000000000000000000000000000000000016178455565b60005b81811061478d575050505050565b6147a061479b82848861466f565b61467f565b6147da610be76147d17fffffffff000000000000000000000000000000000000000000000000000000008416610bb9565b60038801614b4a565b6147e7575060010161477f565b613978906148176118bb7fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000871681565b7fd5d4419e000000000000000000000000000000000000000000000000000000006000527fffffffff000000000000000000000000000000000000000000000000000000009091166004527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016602452604490565b9073ffffffffffffffffffffffffffffffffffffffff16036148b15761477c90614736565b7f50a67a840000000000000000000000000000000000000000000000000000000060005260046000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000061490991168092614b4a565b156149115750565b7f236e22ad0000000000000000000000000000000000000000000000000000000060005260045260246000fd5b91602061153793818152019161321d565b60409073ffffffffffffffffffffffffffffffffffffffff61153794931681528160208201520190610d1d565b9291908161498b575b50509050565b6149b8610be77fb02cc19200000000000000000000000000000000000000000000000000000000866158ea565b614a905773ffffffffffffffffffffffffffffffffffffffff841691823b156101a357614a1892600092836040518096819582947f6d61fe700000000000000000000000000000000000000000000000000000000084526004840161493e565b03925af19081614a75575b50614a6b5750614a31612f80565b90614a676040519283927f1672bd930000000000000000000000000000000000000000000000000000000084526004840161494f565b0390fd5b9050803880614985565b80614a846000614a8a93612322565b806110d7565b38614a23565b7f70560bfc0000000000000000000000000000000000000000000000000000000060005273ffffffffffffffffffffffffffffffffffffffff841660045260246000fd5b92919081614ae25750509050565b6149b8610be77f28171ad000000000000000000000000000000000000000000000000000000000866158ea565b92919081614b1d5750509050565b6149b8610be77fab3e34c100000000000000000000000000000000000000000000000000000000866158ea565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169081158015614c0a575b614c035760016000528060205260406000205480158015614bf9575b15614bd157508181614bcc93614bb6614bc6946001600052602052604060002090565b5590600052602052604060002090565b60019055565b600190565b9180614be7836001600052602052604060002090565b55600052602052604060002055600190565b5060018116614b93565b5050600090565b5081600052806020526040600020541515614b77565b906000600160005282602052604060002054604051915b600182161580614c79575b15614c665760010190808260051b8401526000528360205260406000205490614c37565b6001810160051b83016040528252509150565b50811515614c42565b80518060011c9060005b828110614c995750505050565b80820390828211610514577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116105145781614cdb600193876123a6565b5190614cf2614cea84896123a6565b5191886123a6565b52614cfd82876123a6565b5201614c8c565b614d0d81612345565b91614d1b6040519384612322565b818352601f19614d2a83612345565b01366020850137600060015b838210614d44575050505090565b600052816020526001604060002054917fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000008316614d8182886123a6565b520190614d36565b916020928160808263ffffffff601f19601f850116968060601c835260401c16878201526040808201528260608201520192614dcc575b50509060800181520190565b90816040608094600086601f19899716860101523e01013880614dc0565b91906040600063ffffffff83831c169260601c9483602482015260208151910182875af1603f3d11166020600051141692602051937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc13d0185101615614e4e575050565b60405191601883015260148201526374a1a72c81526040808201523d60608201523d612a0657601c601f19601f3d01166064019101fd5b600193926000929181614e985750505050565b9394929373ffffffffffffffffffffffffffffffffffffffff1690813b15614f3557918491614efe93836040518096819582947f8a91b0e3000000000000000000000000000000000000000000000000000000008452602060048501818152019161321d565b03925af19081614f21575b50614f19575090388080806138ba565b9190506138ce565b83614f2e91949294612322565b9138614f09565b8480fd5b600191825b15614f5e575b600092835260208290526040832080549084905592614f3e565b600183161580614f72575b614f4457915050565b50821515614f69565b60031115614f8557565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91614fbe81614f7b565b806150275750610be7614fd1918361597d565b614fd85750565b7fcf7b49f6000000000000000000000000000000000000000000000000000000006000527fffffffff000000000000000000000000000000000000000000000000000000001660045260246000fd5b80615033600192614f7b565b0361504557610be7614fd19183615a5a565b615052610be7828461597d565b9081615061575b50614fd85750565b6150709150610be79083615a5a565b38615059565b9192908201916020818403126101a35780359067ffffffffffffffff82116101a357019180601f840112156101a357823567ffffffffffffffff81116101a3578160208260051b860101116101a357928082037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019060005b8581106150ff5750505050505050565b60208160051b8301013567ffffffffffffffff81116101a357838112156101a3578201602081013573ffffffffffffffffffffffffffffffffffffffff81169081036101a3573014615155575b506001016150ef565b60608101359067ffffffffffffffff82116101a3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1818703018212156101a3570160208101359063ffffffff82116101a3576040019080860382136101a3576004136101a35735908160e01c63b61d27f6811490811561520b575b506136a95761520586897fffffffff0000000000000000000000000000000000000000000000000000000060019516614fb4565b9061514c565b6334fcd5be915014386151d1565b6152709093929361526a8174ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b9061530a565b9263ffffffff8460401c169360601c9230602484015284604484015260c060a48401528060c4840192835193602085019052838501828060448301526152e8575b505050833b156101a3576152db9260206044600094601f19601f389601160101910183865af11590565b6152e3575050565b613d10565b606490600082601f1986168301015201378038806152b1565b90156122725790565b90600482161561534b575060581b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffff00000000000000001790565b5460601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660389190911b6bffffffff0000000000000000161790565b65ffffffffffff60809392604295806040519586378420927f9b23e06584efc6b65fc854cee55011d89f86485487b6db36aed7d23884711ea38552602085015216604083015260608201522060606040517f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a7946921881524660208201523060408201522090604051917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522090565b91817f1626ba7e000000000000000000000000000000000000000000000000000000009461549383615498957fffffffff0000000000000000000000000000000000000000000000000000000097613db1565b614014565b16036154a057565b7f01334f770000000000000000000000000000000000000000000000000000000060005260046000fd5b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112156101a357016020813591019167ffffffffffffffff82116101a35781360383136101a357565b61153790615695926040519384927f2a3d428c00000000000000000000000000000000000000000000000000000000602085015260006024850152606060448501526155866084850161556c836101a8565b73ffffffffffffffffffffffffffffffffffffffff169052565b602081013560a48501526156646156586155f96155bc6155a960408601866154ca565b61012060c48b01526101a48a019161321d565b6155c960608601866154ca565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c8a84030160e48b015261321d565b608084013561010488015260a084013561012488015260c084013561014488015261562760e08501856154ca565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c898403016101648a015261321d565b916101008101906154ca565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c8684030161018487015261321d565b90606483015203601f198101835282612322565b90929160006084602092601f19601f63ffffffff8960401c169860601c978960248901528061018489015194858a0182808983015261572c575b5050500116010182840182865af1601f3d1116156157045750505060005190565b6004015163ffffffff16632a3d428c0361572257634db96e31613d7e565b6319aed90d613d7e565b60a49089828886168301015201378038806156e3565b73ffffffffffffffffffffffffffffffffffffffff806157628484615837565b93169116171790565b939260016157c36157bc8774ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b809761530a565b955460a01c16156157e7579384611537949551630ab8785f600483015281526156a9565b7fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000857f566f60e2000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b9061584a8260a01c65ffffffffffff1690565b65ffffffffffff8116156158dd575b65ffffffffffff6158ae61587b6158a761587b849561588661587b8960a01c90565b65ffffffffffff1690565b868116156158d6575b8616908616818111156158ca575060a01b9760d01c90565b9460d01c90565b169116818110156158c1575060d01b1790565b60d01b90501790565b60a01b90509760d01c90565b508561588f565b5065ffffffffffff615859565b6000906020926040517fffffffff00000000000000000000000000000000000000000000000000000000858201927f01ffc9a700000000000000000000000000000000000000000000000000000000845216602482015260248152615950604482612322565b5191617530fa6000513d82615971575b508161596a575090565b9050151590565b60201115915038615960565b9061598a8260e01c6130ec565b9182156159f4575b508161599c575090565b600491506159ea9074ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b5460a01c16151590565b60ff919250615a4e907fffffffff00000000000000000000000000000000000000000000000000000000166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd01602052604060002090565b5460a81c169038615992565b906003615ac87fffffffff000000000000000000000000000000000000000000000000000000009274ffffffffffffffffffffffffffffffffffffffffff166000527f596912a710dec01bac203cb0ed2c7e56a2ce6b2a68276967fff6dd57561bdd02602052604060002090565b01911660005260205260406000205415159056fea164736f6c634300081a000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0320000000000000000000000000000000000008e6a39e03c7156e46b238c9e2036
-----Decoded View---------------
Arg [0] : entryPoint (address): 0x0000000071727De22E5E9d8BAf0edAc6f37da032
Arg [1] : executionInstallDelegate (address): 0x0000000000008e6a39E03C7156e46b238C9E2036
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032
Arg [1] : 0000000000000000000000000000000000008e6a39e03c7156e46b238c9e2036
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.