Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Method | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|---|
0x6100513d | 8442613 | 10 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8440963 | 16 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8440883 | 16 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8440305 | 18 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8440213 | 18 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8440169 | 19 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8440102 | 19 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8439965 | 19 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8439942 | 19 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8434055 | 39 hrs ago | Contract Creation | 0 ETH | |||
0x6100513d | 8427641 | 2 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8427012 | 2 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8418034 | 3 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8417542 | 3 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8411728 | 4 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8405556 | 5 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8390091 | 7 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8376348 | 9 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8359736 | 12 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8338982 | 14 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8338970 | 14 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8338203 | 15 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8333419 | 15 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8333141 | 15 days ago | Contract Creation | 0 ETH | |||
0x6100513d | 8330656 | 16 days ago | Contract Creation | 0 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
AccountFactory
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 10000000 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 {ValidationConfigLib} from "@erc6900/reference-implementation/libraries/ValidationConfigLib.sol"; import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {LibClone} from "solady/utils/LibClone.sol"; import {ModularAccount} from "../account/ModularAccount.sol"; import {SemiModularAccountBytecode} from "../account/SemiModularAccountBytecode.sol"; /// @title Account Factory /// @author Alchemy /// @notice Factory contract to deploy modular accounts. Allows creation of both modular and semi-modular accounts /// (the bytecode variant). contract AccountFactory is Ownable2Step { ModularAccount public immutable ACCOUNT_IMPL; SemiModularAccountBytecode public immutable SEMI_MODULAR_ACCOUNT_IMPL; IEntryPoint public immutable ENTRY_POINT; address public immutable SINGLE_SIGNER_VALIDATION_MODULE; address public immutable WEBAUTHN_VALIDATION_MODULE; event ModularAccountDeployed(address indexed account, address indexed owner, uint256 salt); event SemiModularAccountDeployed(address indexed account, address indexed owner, uint256 salt); event WebAuthnModularAccountDeployed( address indexed account, uint256 indexed ownerX, uint256 indexed ownerY, uint256 salt ); error InvalidAction(); error TransferFailed(); constructor( IEntryPoint _entryPoint, ModularAccount _accountImpl, SemiModularAccountBytecode _semiModularImpl, address _singleSignerValidationModule, address _webAuthnValidationModule, address owner ) Ownable(owner) { ENTRY_POINT = _entryPoint; ACCOUNT_IMPL = _accountImpl; SEMI_MODULAR_ACCOUNT_IMPL = _semiModularImpl; SINGLE_SIGNER_VALIDATION_MODULE = _singleSignerValidationModule; WEBAUTHN_VALIDATION_MODULE = _webAuthnValidationModule; } /// @notice Create an account with the single singer validation module installed, and return its address. /// @dev Returns the address even if the account is already deployed. /// Note that during user operation execution, this method is called only if the account is not deployed. /// This method returns an existing account address so that entryPoint.getSenderAddress() would work even after /// account creation. /// @param owner The owner of the account. /// @param salt The salt to use for the account creation. /// @param entityId The entity ID to use for the account creation. /// @return The address of the created account. function createAccount(address owner, uint256 salt, uint32 entityId) external returns (ModularAccount) { bytes32 combinedSalt = getSalt(owner, salt, entityId); // LibClone short-circuits if it's already deployed. (bool alreadyDeployed, address instance) = LibClone.createDeterministicERC1967(address(ACCOUNT_IMPL), combinedSalt); // short circuit if exists if (!alreadyDeployed) { bytes memory moduleInstallData = abi.encode(entityId, owner); // point proxy to actual implementation and init plugins ModularAccount(payable(instance)).initializeWithValidation( ValidationConfigLib.pack(SINGLE_SIGNER_VALIDATION_MODULE, entityId, true, true, true), new bytes4[](0), moduleInstallData, new bytes[](0) ); emit ModularAccountDeployed(instance, owner, salt); } return ModularAccount(payable(instance)); } /// @notice Create a semi-modular account and return its address. /// @dev This only ever deploys semi-modular accounts with added bytecode since this is much less /// expensive than the storage-only variant, which should only be used for upgrades. /// @param owner The owner of the account. /// @param salt The salt to use for the account creation. /// @return The address of the created account. function createSemiModularAccount(address owner, uint256 salt) external returns (SemiModularAccountBytecode) { // both module address and entityId for fallback validations are hardcoded at the maximum value. bytes32 fullSalt = getSalt(owner, salt, type(uint32).max); bytes memory immutables = _getImmutableArgs(owner); // LibClone short-circuits if it's already deployed. (bool alreadyDeployed, address instance) = LibClone.createDeterministicERC1967(address(SEMI_MODULAR_ACCOUNT_IMPL), immutables, fullSalt); if (!alreadyDeployed) { emit SemiModularAccountDeployed(instance, owner, salt); } return SemiModularAccountBytecode(payable(instance)); } /// @notice Create an account with the WebAuthn module installed, and return its address. /// @dev Returns the address even if the account is already deployed. /// Note that during user operation execution, this method is called only if the account is not deployed. /// This method returns an existing account address so that entryPoint.getSenderAddress() would work even after /// account creation. /// @param ownerX The x coordinate of the owner's public key. /// @param ownerY The y coordinate of the owner's public key. /// @param salt The salt to use for the account creation. /// @param entityId The entity ID to use for the account creation. /// @return The address of the created account. function createWebAuthnAccount(uint256 ownerX, uint256 ownerY, uint256 salt, uint32 entityId) external returns (ModularAccount) { bytes32 combinedSalt = getSaltWebAuthn(ownerX, ownerY, salt, entityId); // LibClone short-circuits if it's already deployed. (bool alreadyDeployed, address instance) = LibClone.createDeterministicERC1967(address(ACCOUNT_IMPL), combinedSalt); // short circuit if exists if (!alreadyDeployed) { bytes memory moduleInstallData = abi.encode(entityId, ownerX, ownerY); // point proxy to actual implementation and init plugins ModularAccount(payable(instance)).initializeWithValidation( ValidationConfigLib.pack(WEBAUTHN_VALIDATION_MODULE, entityId, true, true, true), new bytes4[](0), moduleInstallData, new bytes[](0) ); emit WebAuthnModularAccountDeployed(instance, ownerX, ownerY, salt); } return ModularAccount(payable(instance)); } /// @notice Add stake to the entry point contract. /// @param unstakeDelay The delay in seconds before the stake can be withdrawn. function addStake(uint32 unstakeDelay) external payable onlyOwner { ENTRY_POINT.addStake{value: msg.value}(unstakeDelay); } /// @notice Unlock the stake in the entry point contract. function unlockStake() external onlyOwner { ENTRY_POINT.unlockStake(); } /// @notice Withdraw the stake from the entry point contract. /// @param withdrawAddress The address to withdraw the stake to. function withdrawStake(address payable withdrawAddress) external onlyOwner { ENTRY_POINT.withdrawStake(withdrawAddress); } /// @notice Withdraw funds from this contract. /// @dev Can be used to withdraw native currency or ERC-20 tokens. /// @param to The address to withdraw the funds to. /// @param token The address of the token to withdraw, or the zero address for native currency. /// @param amount The amount to withdraw. function withdraw(address payable to, address token, uint256 amount) external onlyOwner { if (token == address(0)) { (bool success,) = to.call{value: address(this).balance}(""); if (!success) { revert TransferFailed(); } } else { SafeERC20.safeTransfer(IERC20(token), to, amount); } } /// @notice Calculate the counterfactual address of this account as it would be returned by createAccount. /// @param owner The owner of the account. /// @param salt The salt to use for the account creation. /// @param entityId The entity ID to use for the account creation. /// @return The address of the account. function getAddress(address owner, uint256 salt, uint32 entityId) external view returns (address) { return LibClone.predictDeterministicAddressERC1967( address(ACCOUNT_IMPL), getSalt(owner, salt, entityId), address(this) ); } /// @notice Calculate the counterfactual address of a semi-modular account as it would be returned by /// createSemiModularAccount. /// @param owner The owner of the account. /// @param salt The salt to use for the account creation. /// @return The address of the account. function getAddressSemiModular(address owner, uint256 salt) external view returns (address) { bytes32 fullSalt = getSalt(owner, salt, type(uint32).max); bytes memory immutables = _getImmutableArgs(owner); return _getAddressSemiModular(immutables, fullSalt); } /// @notice Calculate the counterfactual address of a webauthn account as it would be returned by /// createWebAuthnAccount. /// @param ownerX The x coordinate of the owner's public key. /// @param ownerY The y coordinate of the owner's public key. /// @param salt The salt to use for the account creation. /// @param entityId The entity ID to use for the account creation. /// @return The address of the account. function getAddressWebAuthn(uint256 ownerX, uint256 ownerY, uint256 salt, uint32 entityId) external view returns (address) { return LibClone.predictDeterministicAddressERC1967( address(ACCOUNT_IMPL), getSaltWebAuthn(ownerX, ownerY, salt, entityId), address(this) ); } /// @notice Disable renouncing ownership. function renounceOwnership() public view override onlyOwner { revert InvalidAction(); } /// @notice Get the full salt used for account creation. /// @dev To get the full salt used in createSemiModularAccount, use type(uint32).max for entityId. /// @param owner The owner of the account. /// @param salt The salt to use for the account creation. /// @param entityId The entity ID to use for the account creation. /// @return The full salt. function getSalt(address owner, uint256 salt, uint32 entityId) public pure returns (bytes32) { return keccak256(abi.encodePacked(owner, salt, entityId)); } /// @notice Get the full salt used for account creation using WebAuthn. /// @param ownerX The x coordinate of the owner's public key. /// @param ownerY The y coordinate of the owner's public key. /// @param salt The salt to use for the account creation. /// @param entityId The entity ID to use for the account creation. /// @return The full salt. function getSaltWebAuthn(uint256 ownerX, uint256 ownerY, uint256 salt, uint32 entityId) public pure returns (bytes32) { return keccak256(abi.encodePacked(ownerX, ownerY, salt, entityId)); } function _getAddressSemiModular(bytes memory immutables, bytes32 salt) internal view returns (address) { return LibClone.predictDeterministicAddressERC1967( address(SEMI_MODULAR_ACCOUNT_IMPL), immutables, salt, address(this) ); } function _getImmutableArgs(address owner) private pure returns (bytes memory) { return abi.encodePacked(owner); } }
// 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; } }
/** ** 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; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol) pragma solidity ^0.8.20; import {Ownable} from "./Ownable.sol"; /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is specified at deployment time in the constructor for `Ownable`. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2Step is Ownable { address private _pendingOwner; event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return _pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual override onlyOwner { _pendingOwner = newOwner; emit OwnershipTransferStarted(owner(), newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { delete _pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() public virtual { address sender = _msgSender(); if (pendingOwner() != sender) { revert OwnableUnauthorizedAccount(sender); } _transferOwnership(sender); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Minimal proxy library. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol) /// @author Minimal proxy by 0age (https://github.com/0age) /// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie /// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args) /// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy) /// /// @dev Minimal proxy: /// Although the sw0nt pattern saves 5 gas over the ERC1167 pattern during runtime, /// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern, /// which saves 4 gas over the ERC1167 pattern during runtime, and has the smallest bytecode. /// - Automatically verified on Etherscan. /// /// @dev Minimal proxy (PUSH0 variant): /// This is a new minimal proxy that uses the PUSH0 opcode introduced during Shanghai. /// It is optimized first for minimal runtime gas, then for minimal bytecode. /// The PUSH0 clone functions are intentionally postfixed with a jarring "_PUSH0" as /// many EVM chains may not support the PUSH0 opcode in the early months after Shanghai. /// Please use with caution. /// - Automatically verified on Etherscan. /// /// @dev Clones with immutable args (CWIA): /// The implementation of CWIA here is does NOT append the immutable args into the calldata /// passed into delegatecall. It is simply an ERC1167 minimal proxy with the immutable arguments /// appended to the back of the runtime bytecode. /// - Uses the identity precompile (0x4) to copy args during deployment. /// /// @dev Minimal ERC1967 proxy: /// An minimal ERC1967 proxy, intended to be upgraded with UUPS. /// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic. /// - Automatically verified on Etherscan. /// /// @dev Minimal ERC1967 proxy with immutable args: /// - Uses the identity precompile (0x4) to copy args during deployment. /// - Automatically verified on Etherscan. /// /// @dev ERC1967I proxy: /// An variant of the minimal ERC1967 proxy, with a special code path that activates /// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the /// `implementation` address. The returned implementation is guaranteed to be valid if the /// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`. /// /// @dev ERC1967I proxy with immutable args: /// An variant of the minimal ERC1967 proxy, with a special code path that activates /// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the /// - Uses the identity precompile (0x4) to copy args during deployment. /// /// @dev Minimal ERC1967 beacon proxy: /// A minimal beacon proxy, intended to be upgraded with an upgradable beacon. /// - Automatically verified on Etherscan. /// /// @dev Minimal ERC1967 beacon proxy with immutable args: /// - Uses the identity precompile (0x4) to copy args during deployment. /// - Automatically verified on Etherscan. /// /// @dev ERC1967I beacon proxy: /// An variant of the minimal ERC1967 beacon proxy, with a special code path that activates /// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the /// `implementation` address. The returned implementation is guaranteed to be valid if the /// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`. /// /// @dev ERC1967I proxy with immutable args: /// An variant of the minimal ERC1967 beacon proxy, with a special code path that activates /// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the /// - Uses the identity precompile (0x4) to copy args during deployment. library LibClone { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The keccak256 of deployed code for the clone proxy, /// with the implementation set to `address(0)`. bytes32 internal constant CLONE_CODE_HASH = 0x48db2cfdb2853fce0b464f1f93a1996469459df3ab6c812106074c4106a1eb1f; /// @dev The keccak256 of deployed code for the PUSH0 proxy, /// with the implementation set to `address(0)`. bytes32 internal constant PUSH0_CLONE_CODE_HASH = 0x67bc6bde1b84d66e267c718ba44cf3928a615d29885537955cb43d44b3e789dc; /// @dev The keccak256 of deployed code for the ERC-1167 CWIA proxy, /// with the implementation set to `address(0)`. bytes32 internal constant CWIA_CODE_HASH = 0x3cf92464268225a4513da40a34d967354684c32cd0edd67b5f668dfe3550e940; /// @dev The keccak256 of the deployed code for the ERC1967 proxy. bytes32 internal constant ERC1967_CODE_HASH = 0xaaa52c8cc8a0e3fd27ce756cc6b4e70c51423e9b597b11f32d3e49f8b1fc890d; /// @dev The keccak256 of the deployed code for the ERC1967I proxy. bytes32 internal constant ERC1967I_CODE_HASH = 0xce700223c0d4cea4583409accfc45adac4a093b3519998a9cbbe1504dadba6f7; /// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy. bytes32 internal constant ERC1967_BEACON_PROXY_CODE_HASH = 0x14044459af17bc4f0f5aa2f658cb692add77d1302c29fe2aebab005eea9d1162; /// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy. bytes32 internal constant ERC1967I_BEACON_PROXY_CODE_HASH = 0xf8c46d2793d5aa984eb827aeaba4b63aedcab80119212fce827309788735519a; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Unable to deploy the clone. error DeploymentFailed(); /// @dev The salt must start with either the zero address or `by`. error SaltDoesNotStartWith(); /// @dev The ETH transfer has failed. error ETHTransferFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a clone of `implementation`. function clone(address implementation) internal returns (address instance) { instance = clone(0, implementation); } /// @dev Deploys a clone of `implementation`. /// Deposits `value` ETH during deployment. function clone(uint256 value, address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * --------------------------------------------------------------------------+ * CREATION (9 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * --------------------------------------------------------------------------| * RUNTIME (44 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * | * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 3d | RETURNDATASIZE | 0 0 0 | | * 3d | RETURNDATASIZE | 0 0 0 0 | | * | * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 0 0 | | * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | | * 3d | RETURNDATASIZE | 0 0 cds 0 0 0 0 | | * 37 | CALLDATACOPY | 0 0 0 0 | [0..cds): calldata | * | * ::: delegate call to the implementation contract :::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata | * 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata | * 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata | * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata | * | * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata | * 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata | * 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata | * 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata | * | * 60 0x2a | PUSH1 0x2a | 0x2a success 0 rds | [0..rds): returndata | * 57 | JUMPI | 0 rds | [0..rds): returndata | * | * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * fd | REVERT | | [0..rds): returndata | * | * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * --------------------------------------------------------------------------+ */ mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) instance := create(value, 0x0c, 0x35) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Deploys a deterministic clone of `implementation` with `salt`. function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { instance = cloneDeterministic(0, implementation, salt); } /// @dev Deploys a deterministic clone of `implementation` with `salt`. /// Deposits `value` ETH during deployment. function cloneDeterministic(uint256 value, address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) instance := create2(value, 0x0c, 0x35, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the initialization code of the clone of `implementation`. function initCode(address implementation) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) mstore(add(c, 0x40), 0x5af43d3d93803e602a57fd5bf30000000000000000000000) mstore(add(c, 0x28), implementation) mstore(add(c, 0x14), 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) mstore(c, 0x35) // Store the length. mstore(0x40, add(c, 0x60)) // Allocate memory. } } /// @dev Returns the initialization code hash of the clone of `implementation`. function initCodeHash(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) hash := keccak256(0x0c, 0x35) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the address of the clone of `implementation`, with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress(address implementation, bytes32 salt, address deployer) internal pure returns (address predicted) { bytes32 hash = initCodeHash(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL PROXY OPERATIONS (PUSH0 VARIANT) */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a PUSH0 clone of `implementation`. function clone_PUSH0(address implementation) internal returns (address instance) { instance = clone_PUSH0(0, implementation); } /// @dev Deploys a PUSH0 clone of `implementation`. /// Deposits `value` ETH during deployment. function clone_PUSH0(uint256 value, address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * --------------------------------------------------------------------------+ * CREATION (9 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 5f | PUSH0 | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 5f | PUSH0 | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * --------------------------------------------------------------------------| * RUNTIME (45 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * | * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: | * 5f | PUSH0 | 0 | | * 5f | PUSH0 | 0 0 | | * | * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 | | * 5f | PUSH0 | 0 cds 0 0 | | * 5f | PUSH0 | 0 0 cds 0 0 | | * 37 | CALLDATACOPY | 0 0 | [0..cds): calldata | * | * ::: delegate call to the implementation contract :::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 | [0..cds): calldata | * 5f | PUSH0 | 0 cds 0 0 | [0..cds): calldata | * 73 addr | PUSH20 addr | addr 0 cds 0 0 | [0..cds): calldata | * 5a | GAS | gas addr 0 cds 0 0 | [0..cds): calldata | * f4 | DELEGATECALL | success | [0..cds): calldata | * | * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success | [0..cds): calldata | * 5f | PUSH0 | 0 rds success | [0..cds): calldata | * 5f | PUSH0 | 0 0 rds success | [0..cds): calldata | * 3e | RETURNDATACOPY | success | [0..rds): returndata | * | * 60 0x29 | PUSH1 0x29 | 0x29 success | [0..rds): returndata | * 57 | JUMPI | | [0..rds): returndata | * | * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds | [0..rds): returndata | * 5f | PUSH0 | 0 rds | [0..rds): returndata | * fd | REVERT | | [0..rds): returndata | * | * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | [0..rds): returndata | * 3d | RETURNDATASIZE | rds | [0..rds): returndata | * 5f | PUSH0 | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * --------------------------------------------------------------------------+ */ mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 mstore(0x14, implementation) // 20 mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 instance := create(value, 0x0e, 0x36) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x24, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`. function cloneDeterministic_PUSH0(address implementation, bytes32 salt) internal returns (address instance) { instance = cloneDeterministic_PUSH0(0, implementation, salt); } /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`. /// Deposits `value` ETH during deployment. function cloneDeterministic_PUSH0(uint256 value, address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 mstore(0x14, implementation) // 20 mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 instance := create2(value, 0x0e, 0x36, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x24, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the initialization code of the PUSH0 clone of `implementation`. function initCode_PUSH0(address implementation) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) mstore(add(c, 0x40), 0x5af43d5f5f3e6029573d5ffd5b3d5ff300000000000000000000) // 16 mstore(add(c, 0x26), implementation) // 20 mstore(add(c, 0x12), 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 mstore(c, 0x36) // Store the length. mstore(0x40, add(c, 0x60)) // Allocate memory. } } /// @dev Returns the initialization code hash of the PUSH0 clone of `implementation`. function initCodeHash_PUSH0(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 mstore(0x14, implementation) // 20 mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 hash := keccak256(0x0e, 0x36) mstore(0x24, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the address of the PUSH0 clone of `implementation`, with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress_PUSH0( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHash_PUSH0(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CLONES WITH IMMUTABLE ARGS OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `args`. function clone(address implementation, bytes memory args) internal returns (address instance) { instance = clone(0, implementation, args); } /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `args`. /// Deposits `value` ETH during deployment. function clone(uint256 value, address implementation, bytes memory args) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * ---------------------------------------------------------------------------+ * CREATION (10 bytes) | * ---------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------| * 61 runSize | PUSH2 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------| * RUNTIME (45 bytes + extraLength) | * ---------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------| * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..cds): calldata | * | * ::: delegate call to the implementation contract ::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 0 | [0..cds): calldata | * 36 | CALLDATASIZE | cds 0 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata | * 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata | * 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata | * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata | * | * ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success 0 | [0..cds): calldata | * 82 | DUP3 | 0 rds success 0 | [0..cds): calldata | * 80 | DUP1 | 0 0 rds success 0 | [0..cds): calldata | * 3e | RETURNDATACOPY | success 0 | [0..rds): returndata | * 90 | SWAP1 | 0 success | [0..rds): returndata | * 3d | RETURNDATASIZE | rds 0 success | [0..rds): returndata | * 91 | SWAP2 | success 0 rds | [0..rds): returndata | * | * 60 0x2b | PUSH1 0x2b | 0x2b success 0 rds | [0..rds): returndata | * 57 | JUMPI | 0 rds | [0..rds): returndata | * | * ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * fd | REVERT | | [0..rds): returndata | * | * ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * ---------------------------------------------------------------------------+ */ let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n)) mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3) mstore(add(m, 0x14), implementation) mstore(m, add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(0x88, n))) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`. instance := create(value, add(m, add(0x0b, lt(n, 0xffd3))), add(n, 0x37)) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } // 0,0x5e17b14ADd6c386305A32928F985b29bbA34Eff5, hex"01020304" /// @dev Deploys a deterministic clone of `implementation` /// with immutable arguments encoded in `args` and `salt`. function cloneDeterministic(address implementation, bytes memory args, bytes32 salt) internal returns (address instance) { instance = cloneDeterministic(0, implementation, args, salt); } /// @dev Deploys a deterministic clone of `implementation` /// with immutable arguments encoded in `args` and `salt`. function cloneDeterministic( uint256 value, address implementation, bytes memory args, bytes32 salt ) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n)) mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3) mstore(add(m, 0x14), implementation) mstore(m, add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(0x88, n))) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`. instance := create2(value, add(m, add(0x0b, lt(n, 0xffd3))), add(n, 0x37), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /// @dev Returns the initialization code hash of the clone of `implementation` /// using immutable arguments encoded in `args`. function initCode(address implementation, bytes memory args) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffd2)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(c, 0x57), i), mload(add(add(args, 0x20), i))) } mstore(add(c, 0x37), 0x5af43d82803e903d91602b57fd5bf3) mstore(add(c, 0x28), implementation) mstore(add(c, 0x14), add(0x61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(0x88, n))) mstore(c, add(0x37, n)) // Store the length. mstore(add(c, add(n, 0x57)), 0) // Zeroize the slot after the bytes. mstore(0x40, add(c, add(n, 0x77))) // Allocate memory. } } /// @dev Returns the initialization code hash of the clone of `implementation` /// using immutable arguments encoded in `args`. function initCodeHash(address implementation, bytes memory args) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffd2)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(m, 0x43), i), mload(add(add(args, 0x20), i))) } mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3) mstore(add(m, 0x14), implementation) mstore(m, add(0x61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(0x88, n))) hash := keccak256(add(m, 0x0c), add(n, 0x37)) } } /// @dev Returns the address of the clone of /// `implementation` using immutable arguments encoded in `args`, with `salt`, by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress( address implementation, bytes memory data, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHash(implementation, data); predicted = predictDeterministicAddress(hash, salt, deployer); } /// @dev Equivalent to `argsOnClone(instance, 0, 2 ** 256 - 1)`. function argsOnClone(address instance) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) mstore(args, sub(extcodesize(instance), 0x2d)) // Store the length. extcodecopy(instance, add(args, 0x20), 0x2d, add(mload(args), 0x20)) mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory. } } /// @dev Equivalent to `argsOnClone(instance, start, 2 ** 256 - 1)`. function argsOnClone(address instance, uint256 start) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) let n := sub(extcodesize(instance), 0x2d) extcodecopy(instance, add(args, 0x20), add(start, 0x2d), add(n, 0x20)) mstore(args, mul(sub(n, start), lt(start, n))) // Store the length. mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory. } } /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`. /// `start` and `end` will be clamped to the range `[0, args.length]`. /// The `instance` MUST be deployed via the clone with immutable args functions. /// Otherwise, the behavior is undefined. /// Out-of-gas reverts if `instance` does not have any code. function argsOnClone(address instance, uint256 start, uint256 end) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) if iszero(lt(end, 0xffff)) { end := 0xffff } let d := mul(sub(end, start), lt(start, end)) extcodecopy(instance, args, add(start, 0x0d), add(d, 0x20)) if iszero(and(0xff, mload(add(args, d)))) { let n := sub(extcodesize(instance), 0x2d) returndatacopy(returndatasize(), returndatasize(), shr(64, n)) d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n)))) } mstore(args, d) // Store the length. mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes. mstore(0x40, add(add(args, 0x40), d)) // Allocate memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL ERC1967 PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Note: The ERC1967 proxy here is intended to be upgraded with UUPS. // This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic. /// @dev Deploys a minimal ERC1967 proxy with `implementation`. function deployERC1967(address implementation) internal returns (address instance) { instance = deployERC1967(0, implementation); } /// @dev Deploys a minimal ERC1967 proxy with `implementation`. /// Deposits `value` ETH during deployment. function deployERC1967(uint256 value, address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * ---------------------------------------------------------------------------------+ * CREATION (34 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code | * 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code | * 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code | * 55 | SSTORE | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------------| * RUNTIME (61 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..calldatasize): calldata | * | * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata | * 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata | * 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata | * 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata | * f4 | DELEGATECALL | succ | [0..calldatasize): calldata | * | * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata | * 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata | * 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata | * 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata | * | * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: | * 60 0x38 | PUSH1 0x38 | dest succ | [0..returndatasize): returndata | * 57 | JUMPI | | [0..returndatasize): returndata | * | * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * fd | REVERT | | [0..returndatasize): returndata | * | * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | [0..returndatasize): returndata | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * f3 | RETURN | | [0..returndatasize): returndata | * ---------------------------------------------------------------------------------+ */ let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x20, 0x6009) mstore(0x1e, implementation) mstore(0x0a, 0x603d3d8160223d3973) instance := create(value, 0x21, 0x5f) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`. function deployDeterministicERC1967(address implementation, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967(0, implementation, salt); } /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967(uint256 value, address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x20, 0x6009) mstore(0x1e, implementation) mstore(0x0a, 0x603d3d8160223d3973) instance := create2(value, 0x21, 0x5f, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967(address implementation, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967(0, implementation, salt); } /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967(uint256 value, address implementation, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x20, 0x6009) mstore(0x1e, implementation) mstore(0x0a, 0x603d3d8160223d3973) // Compute and store the bytecode hash. mstore(add(m, 0x35), keccak256(0x21, 0x5f)) mstore(m, shl(88, address())) mstore8(m, 0xff) // Write the prefix. mstore(add(m, 0x15), salt) instance := keccak256(m, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, 0x21, 0x5f, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`. function initCodeERC1967(address implementation) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) mstore(add(c, 0x60), 0x3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f300) mstore(add(c, 0x40), 0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc) mstore(add(c, 0x20), or(shl(24, implementation), 0x600951)) mstore(add(c, 0x09), 0x603d3d8160223d3973) mstore(c, 0x5f) // Store the length. mstore(0x40, add(c, 0x80)) // Allocate memory. } } /// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`. function initCodeHashERC1967(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x20, 0x6009) mstore(0x1e, implementation) mstore(0x0a, 0x603d3d8160223d3973) hash := keccak256(0x21, 0x5f) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the address of the ERC1967 proxy of `implementation`, with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL ERC1967 PROXY WITH IMMUTABLE ARGS OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a minimal ERC1967 proxy with `implementation` and `args`. function deployERC1967(address implementation, bytes memory args) internal returns (address instance) { instance = deployERC1967(0, implementation, args); } /// @dev Deploys a minimal ERC1967 proxy with `implementation` and `args`. /// Deposits `value` ETH during deployment. function deployERC1967(uint256 value, address implementation, bytes memory args) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n)) mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x16, 0x6009) mstore(0x14, implementation) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`. mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n))) mstore(m, mload(0x16)) instance := create(value, m, add(n, 0x60)) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`. function deployDeterministicERC1967(address implementation, bytes memory args, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967(0, implementation, args, salt); } /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967( uint256 value, address implementation, bytes memory args, bytes32 salt ) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n)) mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x16, 0x6009) mstore(0x14, implementation) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`. mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n))) mstore(m, mload(0x16)) instance := create2(value, m, add(n, 0x60), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967(address implementation, bytes memory args, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967(0, implementation, args, salt); } /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967( uint256 value, address implementation, bytes memory args, bytes32 salt ) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n)) mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x16, 0x6009) mstore(0x14, implementation) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`. mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n))) mstore(m, mload(0x16)) // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, keccak256(m, add(n, 0x60))) mstore(0x01, shl(96, address())) mstore(0x15, salt) instance := keccak256(0x00, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, m, add(n, 0x60), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x35, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation` and `args`. function initCodeERC1967(address implementation, bytes memory args) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffc2)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(c, 0x80), i), mload(add(add(args, 0x20), i))) } mstore(add(c, 0x60), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(add(c, 0x40), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(add(c, 0x20), 0x6009) mstore(add(c, 0x1e), implementation) mstore(add(c, 0x0a), add(0x61003d3d8160233d3973, shl(56, n))) mstore(c, add(n, 0x60)) // Store the length. mstore(add(c, add(n, 0x80)), 0) // Zeroize the slot after the bytes. mstore(0x40, add(c, add(n, 0xa0))) // Allocate memory. } } /// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation` and `args`. function initCodeHashERC1967(address implementation, bytes memory args) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffc2)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(m, 0x60), i), mload(add(add(args, 0x20), i))) } mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) mstore(0x16, 0x6009) mstore(0x14, implementation) mstore(0x00, add(0x61003d3d8160233d3973, shl(56, n))) mstore(m, mload(0x16)) hash := keccak256(m, add(n, 0x60)) } } /// @dev Returns the address of the ERC1967 proxy of `implementation`, `args`, with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967( address implementation, bytes memory args, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967(implementation, args); predicted = predictDeterministicAddress(hash, salt, deployer); } /// @dev Equivalent to `argsOnERC1967(instance, start, 2 ** 256 - 1)`. function argsOnERC1967(address instance) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) mstore(args, sub(extcodesize(instance), 0x3d)) // Store the length. extcodecopy(instance, add(args, 0x20), 0x3d, add(mload(args), 0x20)) mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory. } } /// @dev Equivalent to `argsOnERC1967(instance, start, 2 ** 256 - 1)`. function argsOnERC1967(address instance, uint256 start) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) let n := sub(extcodesize(instance), 0x3d) extcodecopy(instance, add(args, 0x20), add(start, 0x3d), add(n, 0x20)) mstore(args, mul(sub(n, start), lt(start, n))) // Store the length. mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory. } } /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`. /// `start` and `end` will be clamped to the range `[0, args.length]`. /// The `instance` MUST be deployed via the ERC1967 with immutable args functions. /// Otherwise, the behavior is undefined. /// Out-of-gas reverts if `instance` does not have any code. function argsOnERC1967(address instance, uint256 start, uint256 end) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) if iszero(lt(end, 0xffff)) { end := 0xffff } let d := mul(sub(end, start), lt(start, end)) extcodecopy(instance, args, add(start, 0x1d), add(d, 0x20)) if iszero(and(0xff, mload(add(args, d)))) { let n := sub(extcodesize(instance), 0x3d) returndatacopy(returndatasize(), returndatasize(), shr(64, n)) d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n)))) } mstore(args, d) // Store the length. mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes. mstore(0x40, add(add(args, 0x40), d)) // Allocate memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC1967I PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Note: This proxy has a special code path that activates if `calldatasize() == 1`. // This code path skips the delegatecall and directly returns the `implementation` address. // The returned implementation is guaranteed to be valid if the keccak256 of the // proxy's code is equal to `ERC1967I_CODE_HASH`. /// @dev Deploys a ERC1967I proxy with `implementation`. function deployERC1967I(address implementation) internal returns (address instance) { instance = deployERC1967I(0, implementation); } /// @dev Deploys a ERC1967I proxy with `implementation`. /// Deposits `value` ETH during deployment. function deployERC1967I(uint256 value, address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * ---------------------------------------------------------------------------------+ * CREATION (34 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code | * 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code | * 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code | * 55 | SSTORE | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------------| * RUNTIME (82 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * | * ::: check calldatasize ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 58 | PC | 1 cds | | * 14 | EQ | eqs | | * 60 0x43 | PUSH1 0x43 | dest eqs | | * 57 | JUMPI | | | * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..calldatasize): calldata | * | * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata | * 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata | * 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata | * 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata | * f4 | DELEGATECALL | succ | [0..calldatasize): calldata | * | * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata | * 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata | * 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata | * 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata | * | * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: | * 60 0x3E | PUSH1 0x3E | dest succ | [0..returndatasize): returndata | * 57 | JUMPI | | [0..returndatasize): returndata | * | * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * fd | REVERT | | [0..returndatasize): returndata | * | * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | [0..returndatasize): returndata | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * f3 | RETURN | | [0..returndatasize): returndata | * | * ::: implementation , return :::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | | * 60 0x20 | PUSH1 0x20 | 32 | | * 60 0x0F | PUSH1 0x0F | o 32 | | * 3d | RETURNDATASIZE | 0 o 32 | | * 39 | CODECOPY | | [0..32): implementation slot | * 3d | RETURNDATASIZE | 0 | [0..32): implementation slot | * 51 | MLOAD | slot | [0..32): implementation slot | * 54 | SLOAD | impl | [0..32): implementation slot | * 3d | RETURNDATASIZE | 0 impl | [0..32): implementation slot | * 52 | MSTORE | | [0..32): implementation address | * 59 | MSIZE | 32 | [0..32): implementation address | * 3d | RETURNDATASIZE | 0 32 | [0..32): implementation address | * f3 | RETURN | | [0..32): implementation address | * ---------------------------------------------------------------------------------+ */ let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation)))) instance := create(value, 0x0c, 0x74) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`. function deployDeterministicERC1967I(address implementation, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967I(0, implementation, salt); } /// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967I(uint256 value, address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation)))) instance := create2(value, 0x0c, 0x74, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967I(address implementation, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967I(0, implementation, salt); } /// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967I(uint256 value, address implementation, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation)))) // Compute and store the bytecode hash. mstore(add(m, 0x35), keccak256(0x0c, 0x74)) mstore(m, shl(88, address())) mstore8(m, 0xff) // Write the prefix. mstore(add(m, 0x15), salt) instance := keccak256(m, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, 0x0c, 0x74, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the initialization code of the ERC1967I proxy of `implementation`. function initCodeERC1967I(address implementation) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) mstore(add(c, 0x74), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(add(c, 0x54), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(add(c, 0x34), 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(add(c, 0x1d), implementation) mstore(add(c, 0x09), 0x60523d8160223d3973) mstore(add(c, 0x94), 0) mstore(c, 0x74) // Store the length. mstore(0x40, add(c, 0xa0)) // Allocate memory. } } /// @dev Returns the initialization code hash of the ERC1967I proxy of `implementation`. function initCodeHashERC1967I(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation)))) hash := keccak256(0x0c, 0x74) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the address of the ERC1967I proxy of `implementation`, with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967I( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967I(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC1967I PROXY WITH IMMUTABLE ARGS OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a minimal ERC1967I proxy with `implementation` and `args`. function deployERC1967I(address implementation, bytes memory args) internal returns (address) { return deployERC1967I(0, implementation, args); } /// @dev Deploys a minimal ERC1967I proxy with `implementation` and `args`. /// Deposits `value` ETH during deployment. function deployERC1967I(uint256 value, address implementation, bytes memory args) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n)) mstore(add(m, 0x6b), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(add(m, 0x4b), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(add(m, 0x2b), 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(add(m, 0x14), implementation) mstore(m, add(0xfe6100523d8160233d3973, shl(56, n))) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. instance := create(value, add(m, add(0x15, lt(n, 0xffae))), add(0x75, n)) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /// @dev Deploys a deterministic ERC1967I proxy with `implementation`, `args`, and `salt`. function deployDeterministicERC1967I(address implementation, bytes memory args, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967I(0, implementation, args, salt); } /// @dev Deploys a deterministic ERC1967I proxy with `implementation`,`args`, and `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967I( uint256 value, address implementation, bytes memory args, bytes32 salt ) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n)) mstore(add(m, 0x6b), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(add(m, 0x4b), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(add(m, 0x2b), 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(add(m, 0x14), implementation) mstore(m, add(0xfe6100523d8160233d3973, shl(56, n))) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. instance := create2(value, add(m, add(0x15, lt(n, 0xffae))), add(0x75, n), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /// @dev Creates a deterministic ERC1967I proxy with `implementation`, `args` and `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967I(address implementation, bytes memory args, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967I(0, implementation, args, salt); } /// @dev Creates a deterministic ERC1967I proxy with `implementation`,`args` and `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967I( uint256 value, address implementation, bytes memory args, bytes32 salt ) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x75), n)) mstore(add(m, 0x55), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(add(m, 0x35), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(add(m, 0x15), 0x5155f3365814604357363d3d373d3d363d7f360894) mstore(0x16, 0x600f) mstore(0x14, implementation) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. mstore(gt(n, 0xffad), add(0xfe6100523d8160233d3973, shl(56, n))) mstore(m, mload(0x16)) // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, keccak256(m, add(n, 0x75))) mstore(0x01, shl(96, address())) mstore(0x15, salt) instance := keccak256(0x00, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, m, add(0x75, n), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x35, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the initialization code of the ERC1967I proxy of `implementation`and `args`. function initCodeERC1967I(address implementation, bytes memory args) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(c, 0x95), i), mload(add(add(args, 0x20), i))) } mstore(add(c, 0x75), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(add(c, 0x55), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(add(c, 0x35), 0x600f5155f3365814604357363d3d373d3d363d7f360894) mstore(add(c, 0x1e), implementation) mstore(add(c, 0x0a), add(0x6100523d8160233d3973, shl(56, n))) mstore(add(c, add(n, 0x95)), 0) mstore(c, add(0x75, n)) // Store the length. mstore(0x40, add(c, add(n, 0xb5))) // Allocate memory. } } /// @dev Returns the initialization code hash of the ERC1967I proxy of `implementation` and `args. function initCodeHashERC1967I(address implementation, bytes memory args) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(m, 0x75), i), mload(add(add(args, 0x20), i))) } mstore(add(m, 0x55), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3) mstore(add(m, 0x35), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4) mstore(add(m, 0x15), 0x5155f3365814604357363d3d373d3d363d7f360894) mstore(0x16, 0x600f) mstore(0x14, implementation) mstore(0x00, add(0x6100523d8160233d3973, shl(56, n))) mstore(m, mload(0x16)) hash := keccak256(m, add(0x75, n)) } } /// @dev Returns the address of the ERC1967I proxy of `implementation`, 'args` with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967I( address implementation, bytes memory args, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967I(implementation, args); predicted = predictDeterministicAddress(hash, salt, deployer); } /// @dev Equivalent to `argsOnERC1967I(instance, start, 2 ** 256 - 1)`. function argsOnERC1967I(address instance) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) mstore(args, sub(extcodesize(instance), 0x52)) // Store the length. extcodecopy(instance, add(args, 0x20), 0x52, add(mload(args), 0x20)) mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory. } } /// @dev Equivalent to `argsOnERC1967I(instance, start, 2 ** 256 - 1)`. function argsOnERC1967I(address instance, uint256 start) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) let n := sub(extcodesize(instance), 0x52) extcodecopy(instance, add(args, 0x20), add(start, 0x52), add(n, 0x20)) mstore(args, mul(sub(n, start), lt(start, n))) // Store the length. mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory. } } /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`. /// `start` and `end` will be clamped to the range `[0, args.length]`. /// The `instance` MUST be deployed via the ERC1967 with immutable args functions. /// Otherwise, the behavior is undefined. /// Out-of-gas reverts if `instance` does not have any code. function argsOnERC1967I(address instance, uint256 start, uint256 end) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) if iszero(lt(end, 0xffff)) { end := 0xffff } let d := mul(sub(end, start), lt(start, end)) extcodecopy(instance, args, add(start, 0x32), add(d, 0x20)) if iszero(and(0xff, mload(add(args, d)))) { let n := sub(extcodesize(instance), 0x52) returndatacopy(returndatasize(), returndatasize(), shr(64, n)) d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n)))) } mstore(args, d) // Store the length. mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes. mstore(0x40, add(add(args, 0x40), d)) // Allocate memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANT ERC1967 BOOTSTRAP OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Note: This enables an ERC1967 proxy to be deployed at a deterministic address // independent of the implementation: // ``` // address bootstrap = LibClone.constantERC1967Bootstrap(); // address instance = LibClone.deployDeterministicERC1967(0, bootstrap, salt); // LibClone.bootstrapConstantERC1967(bootstrap, implementation); // ``` /// @dev Deploys the constant ERC1967 bootstrap if it has not been deployed. function constantERC1967Bootstrap() internal returns (address bootstrap) { bootstrap = constantERC1967BootstrapAddress(); /// @solidity memory-safe-assembly assembly { if iszero(extcodesize(bootstrap)) { mstore(0x20, 0x0894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55) mstore(0x00, 0x60258060093d393df358357f36) if iszero(create2(0, 0x13, 0x2e, 0)) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } } /// @dev Returns the implementation address of the ERC1967 bootstrap for this contract. function constantERC1967BootstrapAddress() internal view returns (address bootstrap) { bytes32 hash = 0xfe1a42b9c571a6a8c083c94ac67b9cfd74e2582923426aa3b762e3431d717cd1; bootstrap = predictDeterministicAddress(hash, bytes32(0), address(this)); } /// @dev Replaces the implementation at `instance`. function bootstrapERC1967(address instance, address implementation) internal { /// @solidity memory-safe-assembly assembly { mstore(0x00, shr(96, shl(96, implementation))) if iszero(call(gas(), instance, 0, 0x00, 0x20, codesize(), 0x00)) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL ERC1967 BEACON PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Note: If you use this proxy, you MUST make sure that the beacon is a // valid ERC1967 beacon. This means that the beacon must always return a valid // address upon a staticcall to `implementation()`, given sufficient gas. // For performance, the deployment operations and the proxy assumes that the // beacon is always valid and will NOT validate it. /// @dev Deploys a minimal ERC1967 beacon proxy. function deployERC1967BeaconProxy(address beacon) internal returns (address instance) { instance = deployERC1967BeaconProxy(0, beacon); } /// @dev Deploys a minimal ERC1967 beacon proxy. /// Deposits `value` ETH during deployment. function deployERC1967BeaconProxy(uint256 value, address beacon) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * ---------------------------------------------------------------------------------+ * CREATION (34 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * 73 beac | PUSH20 beac | beac 0 r | [0..runSize): runtime code | * 60 slotPos | PUSH1 slotPos | slotPos beac 0 r | [0..runSize): runtime code | * 51 | MLOAD | slot beac 0 r | [0..runSize): runtime code | * 55 | SSTORE | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------------| * RUNTIME (82 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..calldatasize): calldata | * | * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata | * | * ~~~~~~~ beacon staticcall sub procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | * 60 0x20 | PUSH1 0x20 | 32 | | * 36 | CALLDATASIZE | cds 32 | | * 60 0x04 | PUSH1 0x04 | 4 cds 32 | | * 36 | CALLDATASIZE | cds 4 cds 32 | | * 63 0x5c60da1b | PUSH4 0x5c60da1b | 0x5c60da1b cds 4 cds 32 | | * 60 0xe0 | PUSH1 0xe0 | 224 0x5c60da1b cds 4 cds 32 | | * 1b | SHL | sel cds 4 cds 32 | | * 36 | CALLDATASIZE | cds sel cds 4 cds 32 | | * 52 | MSTORE | cds 4 cds 32 | sel | * 7f slot | PUSH32 slot | s cds 4 cds 32 | sel | * 54 | SLOAD | beac cds 4 cds 32 | sel | * 5a | GAS | g beac cds 4 cds 32 | sel | * fa | STATICCALL | succ | impl | * 50 | POP | | impl | * 36 | CALLDATASIZE | cds | impl | * 51 | MLOAD | impl | impl | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | * 5a | GAS | g impl 0 cds 0 0 | [0..calldatasize): calldata | * f4 | DELEGATECALL | succ | [0..calldatasize): calldata | * | * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata | * 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata | * 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata | * 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata | * | * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: | * 60 0x4d | PUSH1 0x4d | dest succ | [0..returndatasize): returndata | * 57 | JUMPI | | [0..returndatasize): returndata | * | * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * fd | REVERT | | [0..returndatasize): returndata | * | * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | [0..returndatasize): returndata | * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | * f3 | RETURN | | [0..returndatasize): returndata | * ---------------------------------------------------------------------------------+ */ let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon)))) instance := create(value, 0x0c, 0x74) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `salt`. function deployDeterministicERC1967BeaconProxy(address beacon, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967BeaconProxy(0, beacon, salt); } /// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967BeaconProxy(uint256 value, address beacon, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon)))) instance := create2(value, 0x0c, 0x74, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Creates a deterministic minimal ERC1967 beacon proxy with `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967BeaconProxy(address beacon, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967BeaconProxy(0, beacon, salt); } /// @dev Creates a deterministic minimal ERC1967 beacon proxy with `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967BeaconProxy(uint256 value, address beacon, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon)))) // Compute and store the bytecode hash. mstore(add(m, 0x35), keccak256(0x0c, 0x74)) mstore(m, shl(88, address())) mstore8(m, 0xff) // Write the prefix. mstore(add(m, 0x15), salt) instance := keccak256(m, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, 0x0c, 0x74, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the initialization code of the minimal ERC1967 beacon proxy. function initCodeERC1967BeaconProxy(address beacon) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) mstore(add(c, 0x74), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(add(c, 0x54), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(add(c, 0x34), 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(add(c, 0x1d), beacon) mstore(add(c, 0x09), 0x60523d8160223d3973) mstore(add(c, 0x94), 0) mstore(c, 0x74) // Store the length. mstore(0x40, add(c, 0xa0)) // Allocate memory. } } /// @dev Returns the initialization code hash of the minimal ERC1967 beacon proxy. function initCodeHashERC1967BeaconProxy(address beacon) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon)))) hash := keccak256(0x0c, 0x74) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the address of the ERC1967 beacon proxy, with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967BeaconProxy( address beacon, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967BeaconProxy(beacon); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC1967 BEACON PROXY WITH IMMUTABLE ARGS OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a minimal ERC1967 beacon proxy with `args`. function deployERC1967BeaconProxy(address beacon, bytes memory args) internal returns (address instance) { instance = deployERC1967BeaconProxy(0, beacon, args); } /// @dev Deploys a minimal ERC1967 beacon proxy with `args`. /// Deposits `value` ETH during deployment. function deployERC1967BeaconProxy(uint256 value, address beacon, bytes memory args) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n)) mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(add(m, 0x14), beacon) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n))) instance := create(value, add(m, 0x16), add(n, 0x75)) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `args` and `salt`. function deployDeterministicERC1967BeaconProxy(address beacon, bytes memory args, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967BeaconProxy(0, beacon, args, salt); } /// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `args` and `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967BeaconProxy( uint256 value, address beacon, bytes memory args, bytes32 salt ) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n)) mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(add(m, 0x14), beacon) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n))) instance := create2(value, add(m, 0x16), add(n, 0x75), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /// @dev Creates a deterministic minimal ERC1967 beacon proxy with `args` and `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967BeaconProxy(address beacon, bytes memory args, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967BeaconProxy(0, beacon, args, salt); } /// @dev Creates a deterministic minimal ERC1967 beacon proxy with `args` and `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967BeaconProxy( uint256 value, address beacon, bytes memory args, bytes32 salt ) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n)) mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(add(m, 0x14), beacon) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n))) // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, keccak256(add(m, 0x16), add(n, 0x75))) mstore(0x01, shl(96, address())) mstore(0x15, salt) instance := keccak256(0x00, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, add(m, 0x16), add(n, 0x75), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x35, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the initialization code of the minimal ERC1967 beacon proxy. function initCodeERC1967BeaconProxy(address beacon, bytes memory args) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(c, 0x95), i), mload(add(add(args, 0x20), i))) } mstore(add(c, 0x75), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(add(c, 0x55), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(add(c, 0x35), 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(add(c, 0x1e), beacon) mstore(add(c, 0x0a), add(0x6100523d8160233d3973, shl(56, n))) mstore(c, add(n, 0x75)) // Store the length. mstore(add(c, add(n, 0x95)), 0) // Zeroize the slot after the bytes. mstore(0x40, add(c, add(n, 0xb5))) // Allocate memory. } } /// @dev Returns the initialization code hash of the minimal ERC1967 beacon proxy with `args`. function initCodeHashERC1967BeaconProxy(address beacon, bytes memory args) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(m, 0x8b), i), mload(add(add(args, 0x20), i))) } mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3) mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c) mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da) mstore(add(m, 0x14), beacon) mstore(m, add(0x6100523d8160233d3973, shl(56, n))) hash := keccak256(add(m, 0x16), add(n, 0x75)) } } /// @dev Returns the address of the ERC1967 beacon proxy with `args`, with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967BeaconProxy( address beacon, bytes memory args, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967BeaconProxy(beacon, args); predicted = predictDeterministicAddress(hash, salt, deployer); } /// @dev Equivalent to `argsOnERC1967BeaconProxy(instance, start, 2 ** 256 - 1)`. function argsOnERC1967BeaconProxy(address instance) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) mstore(args, sub(extcodesize(instance), 0x52)) // Store the length. extcodecopy(instance, add(args, 0x20), 0x52, add(mload(args), 0x20)) mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory. } } /// @dev Equivalent to `argsOnERC1967BeaconProxy(instance, start, 2 ** 256 - 1)`. function argsOnERC1967BeaconProxy(address instance, uint256 start) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) let n := sub(extcodesize(instance), 0x52) extcodecopy(instance, add(args, 0x20), add(start, 0x52), add(n, 0x20)) mstore(args, mul(sub(n, start), lt(start, n))) // Store the length. mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory. } } /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`. /// `start` and `end` will be clamped to the range `[0, args.length]`. /// The `instance` MUST be deployed via the ERC1967 beacon proxy with immutable args functions. /// Otherwise, the behavior is undefined. /// Out-of-gas reverts if `instance` does not have any code. function argsOnERC1967BeaconProxy(address instance, uint256 start, uint256 end) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) if iszero(lt(end, 0xffff)) { end := 0xffff } let d := mul(sub(end, start), lt(start, end)) extcodecopy(instance, args, add(start, 0x32), add(d, 0x20)) if iszero(and(0xff, mload(add(args, d)))) { let n := sub(extcodesize(instance), 0x52) returndatacopy(returndatasize(), returndatasize(), shr(64, n)) d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n)))) } mstore(args, d) // Store the length. mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes. mstore(0x40, add(add(args, 0x40), d)) // Allocate memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC1967I BEACON PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Note: This proxy has a special code path that activates if `calldatasize() == 1`. // This code path skips the delegatecall and directly returns the `implementation` address. // The returned implementation is guaranteed to be valid if the keccak256 of the // proxy's code is equal to `ERC1967_BEACON_PROXY_CODE_HASH`. // // If you use this proxy, you MUST make sure that the beacon is a // valid ERC1967 beacon. This means that the beacon must always return a valid // address upon a staticcall to `implementation()`, given sufficient gas. // For performance, the deployment operations and the proxy assumes that the // beacon is always valid and will NOT validate it. /// @dev Deploys a ERC1967I beacon proxy. function deployERC1967IBeaconProxy(address beacon) internal returns (address instance) { instance = deployERC1967IBeaconProxy(0, beacon); } /// @dev Deploys a ERC1967I beacon proxy. /// Deposits `value` ETH during deployment. function deployERC1967IBeaconProxy(uint256 value, address beacon) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * ---------------------------------------------------------------------------------+ * CREATION (34 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * 73 beac | PUSH20 beac | beac 0 r | [0..runSize): runtime code | * 60 slotPos | PUSH1 slotPos | slotPos beac 0 r | [0..runSize): runtime code | * 51 | MLOAD | slot beac 0 r | [0..runSize): runtime code | * 55 | SSTORE | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------------| * RUNTIME (87 bytes) | * ---------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------| * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..calldatasize): calldata | * | * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata | * | * ~~~~~~~ beacon staticcall sub procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | * 60 0x20 | PUSH1 0x20 | 32 | | * 36 | CALLDATASIZE | cds 32 | | * 60 0x04 | PUSH1 0x04 | 4 cds 32 | | * 36 | CALLDATASIZE | cds 4 cds 32 | | * 63 0x5c60da1b | PUSH4 0x5c60da1b | 0x5c60da1b cds 4 cds 32 | | * 60 0xe0 | PUSH1 0xe0 | 224 0x5c60da1b cds 4 cds 32 | | * 1b | SHL | sel cds 4 cds 32 | | * 36 | CALLDATASIZE | cds sel cds 4 cds 32 | | * 52 | MSTORE | cds 4 cds 32 | sel | * 7f slot | PUSH32 slot | s cds 4 cds 32 | sel | * 54 | SLOAD | beac cds 4 cds 32 | sel | * 5a | GAS | g beac cds 4 cds 32 | sel | * fa | STATICCALL | succ | impl | * ~~~~~~ check calldatasize ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | * 36 | CALLDATASIZE | cds succ | | * 14 | EQ | | impl | * 60 0x52 | PUSH1 0x52 | | impl | * 57 | JUMPI | | impl | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | * 36 | CALLDATASIZE | cds | impl | * 51 | MLOAD | impl | impl | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | * 5a | GAS | g impl 0 cds 0 0 | [0..calldatasize): calldata | * f4 | DELEGATECALL | succ | [0..calldatasize): calldata | * | * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata | * 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata | * 60 0x01 | PUSH1 0x01 | 1 0 rds succ | [0..calldatasize): calldata | * 3e | RETURNDATACOPY | succ | [1..returndatasize): returndata | * | * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: | * 60 0x52 | PUSH1 0x52 | dest succ | [1..returndatasize): returndata | * 57 | JUMPI | | [1..returndatasize): returndata | * | * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds | [1..returndatasize): returndata | * 60 0x01 | PUSH1 0x01 | 1 rds | [1..returndatasize): returndata | * fd | REVERT | | [1..returndatasize): returndata | * | * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | [1..returndatasize): returndata | * 3d | RETURNDATASIZE | rds | [1..returndatasize): returndata | * 60 0x01 | PUSH1 0x01 | 1 rds | [1..returndatasize): returndata | * f3 | RETURN | | [1..returndatasize): returndata | * ---------------------------------------------------------------------------------+ */ let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon)))) instance := create(value, 0x07, 0x79) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Deploys a deterministic ERC1967I beacon proxy with `salt`. function deployDeterministicERC1967IBeaconProxy(address beacon, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967IBeaconProxy(0, beacon, salt); } /// @dev Deploys a deterministic ERC1967I beacon proxy with `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967IBeaconProxy(uint256 value, address beacon, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon)))) instance := create2(value, 0x07, 0x79, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Creates a deterministic ERC1967I beacon proxy with `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967IBeaconProxy(address beacon, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967IBeaconProxy(0, beacon, salt); } /// @dev Creates a deterministic ERC1967I beacon proxy with `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967IBeaconProxy(uint256 value, address beacon, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon)))) // Compute and store the bytecode hash. mstore(add(m, 0x35), keccak256(0x07, 0x79)) mstore(m, shl(88, address())) mstore8(m, 0xff) // Write the prefix. mstore(add(m, 0x15), salt) instance := keccak256(m, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, 0x07, 0x79, salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the initialization code of the ERC1967I beacon proxy. function initCodeERC1967IBeaconProxy(address beacon) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) mstore(add(c, 0x79), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(add(c, 0x59), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(add(c, 0x39), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(add(c, 0x1d), beacon) mstore(add(c, 0x09), 0x60573d8160223d3973) mstore(add(c, 0x99), 0) mstore(c, 0x79) // Store the length. mstore(0x40, add(c, 0xa0)) // Allocate memory. } } /// @dev Returns the initialization code hash of the ERC1967I beacon proxy. function initCodeHashERC1967IBeaconProxy(address beacon) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon)))) hash := keccak256(0x07, 0x79) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero slot. } } /// @dev Returns the address of the ERC1967I beacon proxy, with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967IBeaconProxy( address beacon, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967IBeaconProxy(beacon); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC1967I BEACON PROXY WITH IMMUTABLE ARGS OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a ERC1967I beacon proxy with `args. function deployERC1967IBeaconProxy(address beacon, bytes memory args) internal returns (address instance) { instance = deployERC1967IBeaconProxy(0, beacon, args); } /// @dev Deploys a ERC1967I beacon proxy with `args. /// Deposits `value` ETH during deployment. function deployERC1967IBeaconProxy(uint256 value, address beacon, bytes memory args) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n)) mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(add(m, 0x14), beacon) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`. mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n))) instance := create(value, add(m, 0x16), add(n, 0x7a)) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /// @dev Deploys a deterministic ERC1967I beacon proxy with `args` and `salt`. function deployDeterministicERC1967IBeaconProxy(address beacon, bytes memory args, bytes32 salt) internal returns (address instance) { instance = deployDeterministicERC1967IBeaconProxy(0, beacon, args, salt); } /// @dev Deploys a deterministic ERC1967I beacon proxy with `args` and `salt`. /// Deposits `value` ETH during deployment. function deployDeterministicERC1967IBeaconProxy( uint256 value, address beacon, bytes memory args, bytes32 salt ) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n)) mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(add(m, 0x14), beacon) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`. mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n))) instance := create2(value, add(m, 0x16), add(n, 0x7a), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } } } /// @dev Creates a deterministic ERC1967I beacon proxy with `args` and `salt`. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967IBeaconProxy(address beacon, bytes memory args, bytes32 salt) internal returns (bool alreadyDeployed, address instance) { return createDeterministicERC1967IBeaconProxy(0, beacon, args, salt); } /// @dev Creates a deterministic ERC1967I beacon proxy with `args` and `salt`. /// Deposits `value` ETH during deployment. /// Note: This method is intended for use in ERC4337 factories, /// which are expected to NOT revert if the proxy is already deployed. function createDeterministicERC1967IBeaconProxy( uint256 value, address beacon, bytes memory args, bytes32 salt ) internal returns (bool alreadyDeployed, address instance) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let n := mload(args) pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n)) mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(add(m, 0x14), beacon) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`. mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n))) // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, keccak256(add(m, 0x16), add(n, 0x7a))) mstore(0x01, shl(96, address())) mstore(0x15, salt) instance := keccak256(0x00, 0x55) for {} 1 {} { if iszero(extcodesize(instance)) { instance := create2(value, add(m, 0x16), add(n, 0x7a), salt) if iszero(instance) { mstore(0x00, 0x30116425) // `DeploymentFailed()`. revert(0x1c, 0x04) } break } alreadyDeployed := 1 if iszero(value) { break } if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } break } mstore(0x35, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the initialization code of the ERC1967I beacon proxy with `args`. function initCodeERC1967IBeaconProxy(address beacon, bytes memory args) internal pure returns (bytes memory c) { /// @solidity memory-safe-assembly assembly { c := mload(0x40) let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffa8)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(c, 0x9a), i), mload(add(add(args, 0x20), i))) } mstore(add(c, 0x7a), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(add(c, 0x5a), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(add(c, 0x3a), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(add(c, 0x1e), beacon) mstore(add(c, 0x0a), add(0x6100573d8160233d3973, shl(56, n))) mstore(add(c, add(n, 0x9a)), 0) mstore(c, add(n, 0x7a)) // Store the length. mstore(0x40, add(c, add(n, 0xba))) // Allocate memory. } } /// @dev Returns the initialization code hash of the ERC1967I beacon proxy with `args`. function initCodeHashERC1967IBeaconProxy(address beacon, bytes memory args) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { let c := mload(0x40) // Cache the free memory pointer. let n := mload(args) // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`. returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffa8)) for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { mstore(add(add(c, 0x90), i), mload(add(add(args, 0x20), i))) } mstore(add(c, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3) mstore(add(c, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513) mstore(add(c, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36) mstore(add(c, 0x14), beacon) mstore(c, add(0x6100573d8160233d3973, shl(56, n))) hash := keccak256(add(c, 0x16), add(n, 0x7a)) } } /// @dev Returns the address of the ERC1967I beacon proxy, with `args` and salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddressERC1967IBeaconProxy( address beacon, bytes memory args, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHashERC1967IBeaconProxy(beacon, args); predicted = predictDeterministicAddress(hash, salt, deployer); } /// @dev Equivalent to `argsOnERC1967IBeaconProxy(instance, start, 2 ** 256 - 1)`. function argsOnERC1967IBeaconProxy(address instance) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) mstore(args, sub(extcodesize(instance), 0x57)) // Store the length. extcodecopy(instance, add(args, 0x20), 0x57, add(mload(args), 0x20)) mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory. } } /// @dev Equivalent to `argsOnERC1967IBeaconProxy(instance, start, 2 ** 256 - 1)`. function argsOnERC1967IBeaconProxy(address instance, uint256 start) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) let n := sub(extcodesize(instance), 0x57) extcodecopy(instance, add(args, 0x20), add(start, 0x57), add(n, 0x20)) mstore(args, mul(sub(n, start), lt(start, n))) // Store the length. mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory. } } /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`. /// `start` and `end` will be clamped to the range `[0, args.length]`. /// The `instance` MUST be deployed via the ERC1967I beacon proxy with immutable args functions. /// Otherwise, the behavior is undefined. /// Out-of-gas reverts if `instance` does not have any code. function argsOnERC1967IBeaconProxy(address instance, uint256 start, uint256 end) internal view returns (bytes memory args) { /// @solidity memory-safe-assembly assembly { args := mload(0x40) if iszero(lt(end, 0xffff)) { end := 0xffff } let d := mul(sub(end, start), lt(start, end)) extcodecopy(instance, args, add(start, 0x37), add(d, 0x20)) if iszero(and(0xff, mload(add(args, d)))) { let n := sub(extcodesize(instance), 0x57) returndatacopy(returndatasize(), returndatasize(), shr(64, n)) d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n)))) } mstore(args, d) // Store the length. mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes. mstore(0x40, add(add(args, 0x40), d)) // Allocate memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OTHER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns `address(0)` if the implementation address cannot be determined. function implementationOf(address instance) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { for { extcodecopy(instance, 0x00, 0x00, 0x57) } 1 {} { if mload(0x2d) { // ERC1967I and ERC1967IBeaconProxy detection. if or( eq(keccak256(0x00, 0x52), ERC1967I_CODE_HASH), eq(keccak256(0x00, 0x57), ERC1967I_BEACON_PROXY_CODE_HASH) ) { pop(staticcall(gas(), instance, 0x00, 0x01, 0x00, 0x20)) result := mload(0x0c) break } } // 0age clone detection. result := mload(0x0b) codecopy(0x0b, codesize(), 0x14) // Zeroize the 20 bytes for the address. if iszero(xor(keccak256(0x00, 0x2c), CLONE_CODE_HASH)) { break } mstore(0x0b, result) // Restore the zeroized memory. // CWIA detection. result := mload(0x0a) codecopy(0x0a, codesize(), 0x14) // Zeroize the 20 bytes for the address. if iszero(xor(keccak256(0x00, 0x2d), CWIA_CODE_HASH)) { break } mstore(0x0a, result) // Restore the zeroized memory. // PUSH0 clone detection. result := mload(0x09) codecopy(0x09, codesize(), 0x14) // Zeroize the 20 bytes for the address. result := shr(xor(keccak256(0x00, 0x2d), PUSH0_CLONE_CODE_HASH), result) break } result := shr(96, result) mstore(0x37, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the address when a contract with initialization code hash, /// `hash`, is deployed with `salt`, by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, hash) mstore(0x01, shl(96, deployer)) mstore(0x15, salt) predicted := keccak256(0x00, 0x55) mstore(0x35, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Requires that `salt` starts with either the zero address or `by`. function checkStartsWith(bytes32 salt, address by) internal pure { /// @solidity memory-safe-assembly assembly { // If the salt does not start with the zero address or `by`. if iszero(or(iszero(shr(96, salt)), eq(shr(96, shl(96, by)), shr(96, salt)))) { mstore(0x00, 0x0c4549ef) // `SaltDoesNotStartWith()`. 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; 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); } }
// 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} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol"; import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {LibClone} from "solady/utils/LibClone.sol"; import {ExecutionInstallDelegate} from "../helpers/ExecutionInstallDelegate.sol"; import {SemiModularAccountBase} from "./SemiModularAccountBase.sol"; /// @title Semi-Modular Account Bytecode /// @author Alchemy /// @notice An implementation of a semi-modular account which reads the signer from proxy bytecode if it is not /// disabled and zero in storage. /// @dev Inherits SemiModularAccountBase. This account requires that its proxy is compliant with Solady's LibClone /// ERC1967WithImmutableArgs bytecode with a bytecode-appended address (should be encodePacked) to be used as the /// fallback signer. contract SemiModularAccountBytecode is SemiModularAccountBase { constructor(IEntryPoint entryPoint, ExecutionInstallDelegate executionInstallDelegate) SemiModularAccountBase(entryPoint, executionInstallDelegate) {} /// @inheritdoc IModularAccount function accountId() external pure override returns (string memory) { return "alchemy.sma-bytecode.1.0.0"; } /// @dev If the fallback signer is set in storage, we ignore the bytecode signer. function _retrieveFallbackSignerUnchecked(SemiModularAccountStorage storage _storage) internal view override returns (address) { address storageFallbackSigner = _storage.fallbackSigner; if (storageFallbackSigner != address(0)) { return storageFallbackSigner; } // If the signer in storage is zero, default to bytes memory appendedData = LibClone.argsOnERC1967(address(this), 0, 20); return address(uint160(bytes20(appendedData))); } }
// 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); }
// 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: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// 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(); } } }
// 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; } }
// 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 {ModuleEntity, ValidationConfig} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol"; import {ModuleEntityLib} from "@erc6900/reference-implementation/libraries/ModuleEntityLib.sol"; import {ValidationConfigLib} from "@erc6900/reference-implementation/libraries/ValidationConfigLib.sol"; import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import {FALLBACK_VALIDATION_ID, FALLBACK_VALIDATION_LOOKUP_KEY} from "../helpers/Constants.sol"; import {ExecutionInstallDelegate} from "../helpers/ExecutionInstallDelegate.sol"; import {SignatureType} from "../helpers/SignatureType.sol"; import {RTCallBuffer, SigCallBuffer, UOCallBuffer} from "../libraries/ExecutionLib.sol"; import {ValidationLocatorLib, ValidationLookupKey} from "../libraries/ValidationLocatorLib.sol"; import {ModularAccountBase} from "./ModularAccountBase.sol"; /// @title Semi-Modular Account Base /// @author Alchemy /// @notice Abstract base contract for the Alchemy Semi-Modular Account variants. Includes fallback signer /// functionality. /// @dev Inherits ModularAccountBase. Overrides certain functionality from ModularAccountBase, and exposes an /// internal virtual getter for the fallback signer. abstract contract SemiModularAccountBase is ModularAccountBase { using MessageHashUtils for bytes32; using ModuleEntityLib for ModuleEntity; using ValidationConfigLib for ValidationConfig; struct SemiModularAccountStorage { address fallbackSigner; bool fallbackSignerDisabled; } // keccak256("ReplaySafeHash(bytes32 hash)") bytes32 private constant _REPLAY_SAFE_HASH_TYPEHASH = 0x294a8735843d4afb4f017c76faf3b7731def145ed0025fc9b1d5ce30adf113ff; // keccak256("ERC6900.SemiModularAccount.Storage") uint256 internal constant _SEMI_MODULAR_ACCOUNT_STORAGE_SLOT = 0x5b9dc9aa943f8fa2653ceceda5e3798f0686455280432166ba472eca0bc17a32; uint256 internal constant _SIG_VALIDATION_PASSED = 0; uint256 internal constant _SIG_VALIDATION_FAILED = 1; event FallbackSignerUpdated(address indexed newFallbackSigner, bool isDisabled); error FallbackSignerMismatch(); error FallbackValidationInstallationNotAllowed(); error FallbackSignerDisabled(); error InvalidSignatureType(); constructor(IEntryPoint entryPoint, ExecutionInstallDelegate executionInstallDelegate) ModularAccountBase(entryPoint, executionInstallDelegate) {} /// @notice Updates the fallback signer data in storage. /// @param fallbackSigner The new signer to set. /// @param isDisabled Whether to disable fallback signing entirely. function updateFallbackSignerData(address fallbackSigner, bool isDisabled) external wrapNativeFunction { SemiModularAccountStorage storage _storage = _getSemiModularAccountStorage(); _storage.fallbackSigner = fallbackSigner; _storage.fallbackSignerDisabled = isDisabled; emit FallbackSignerUpdated(fallbackSigner, isDisabled); } function installValidation( ValidationConfig validationConfig, bytes4[] calldata selectors, bytes calldata installData, bytes[] calldata hooks ) external override wrapNativeFunction { // Previously, it was possible to "alias" the fallback validation by installing a module at the reserved // validation entity id 0. Not failing here could cause unexpected behavior, so this is checked to // explicitly revert and warn the caller that this operation would not do what is requested. // // Note that this state can still be reached by upgrading from MA to SMA, but should be handled with // initialization and de-init steps. if (validationConfig.entityId() == FALLBACK_VALIDATION_ID && validationConfig.module() != address(0)) { revert FallbackValidationInstallationNotAllowed(); } _installValidation(validationConfig, selectors, installData, hooks); } /// @notice Returns the fallback signer data in storage. /// @return The fallback signer and a boolean, true if the fallback signer validation is disabled, false if it /// is enabled. function getFallbackSignerData() external view returns (address, bool) { SemiModularAccountStorage storage _storage = _getSemiModularAccountStorage(); return (_retrieveFallbackSignerUnchecked(_storage), _storage.fallbackSignerDisabled); } function _execUserOpValidation( ValidationLookupKey validationLookupKey, bytes32 userOpHash, bytes calldata signatureSegment, UOCallBuffer callBuffer ) internal override returns (uint256) { if (validationLookupKey.eq(FALLBACK_VALIDATION_LOOKUP_KEY)) { address fallbackSigner = _getFallbackSigner(); if (_checkSignature(fallbackSigner, userOpHash.toEthSignedMessageHash(), signatureSegment)) { return _SIG_VALIDATION_PASSED; } return _SIG_VALIDATION_FAILED; } return super._execUserOpValidation(validationLookupKey, userOpHash, signatureSegment, callBuffer); } function _execRuntimeValidation( ValidationLookupKey validationLookupKey, RTCallBuffer callBuffer, bytes calldata authorization ) internal override { if (validationLookupKey.eq(FALLBACK_VALIDATION_LOOKUP_KEY)) { address fallbackSigner = _getFallbackSigner(); if (msg.sender != fallbackSigner) { revert FallbackSignerMismatch(); } } else { super._execRuntimeValidation(validationLookupKey, callBuffer, authorization); } } function _exec1271Validation( SigCallBuffer buffer, bytes32 hash, ValidationLookupKey validationLookupKey, bytes calldata signature ) internal view override returns (bytes4) { if (validationLookupKey.eq(FALLBACK_VALIDATION_LOOKUP_KEY)) { address fallbackSigner = _getFallbackSigner(); // If called during validateUserOp, this implies that we're doing a deferred validation installation. // In this case, as the hash is already replay-safe, we don't need to wrap it. if (msg.sig != this.validateUserOp.selector) { hash = _replaySafeHash(hash); } if (_checkSignature(fallbackSigner, hash, signature)) { return _1271_MAGIC_VALUE; } return _1271_INVALID; } return super._exec1271Validation(buffer, hash, validationLookupKey, signature); } function _checkSignature(address owner, bytes32 digest, bytes calldata sig) internal view returns (bool) { if (sig.length < 1) { revert InvalidSignatureType(); } SignatureType sigType = SignatureType(uint8(bytes1(sig))); sig = sig[1:]; if (sigType == SignatureType.EOA) { (address recovered, ECDSA.RecoverError err,) = ECDSA.tryRecover(digest, sig); if (err == ECDSA.RecoverError.NoError && recovered == owner) { return true; } return false; } else if (sigType == SignatureType.CONTRACT_OWNER) { return SignatureChecker.isValidERC1271SignatureNow(owner, digest, sig); } revert InvalidSignatureType(); } function _isValidationGlobal(ValidationLookupKey validationFunction) internal view override returns (bool) { if (validationFunction.eq(FALLBACK_VALIDATION_LOOKUP_KEY) || super._isValidationGlobal(validationFunction)) { return true; } // At this point, the validation is not the fallback, and it's not an installed global validation. SemiModularAccountStorage storage smaStorage = _getSemiModularAccountStorage(); // Before checking direct-call validation, we return false if fallback validation is disabled. if (smaStorage.fallbackSignerDisabled) { return false; } // Retrieve the fallback signer. address fallbackSigner = _retrieveFallbackSignerUnchecked(smaStorage); // Compute the direct call validation key. ValidationLookupKey fallbackDirectCallValidation = ValidationLocatorLib.directCallLookupKey(fallbackSigner); // Return true if the validation function passed is the fallback direct call validation key, and the sender // is the fallback signer. This enforces that context is a return validationFunction.eq(fallbackDirectCallValidation) && msg.sender == fallbackSigner; } function _getFallbackSigner() internal view returns (address) { SemiModularAccountStorage storage _storage = _getSemiModularAccountStorage(); if (_storage.fallbackSignerDisabled) { revert FallbackSignerDisabled(); } // This can return zero. return _retrieveFallbackSignerUnchecked(_storage); } /// @dev SMA implementations must implement their own fallback signer getter. /// /// NOTE: The passed storage pointer may point to a struct with a zero address signer. It's up /// to inheritors to determine what to do with that information. No assumptions about storage /// state are safe to make besides layout. function _retrieveFallbackSignerUnchecked(SemiModularAccountStorage storage _storage) internal view virtual returns (address) { return _storage.fallbackSigner; } /// @notice Returns the replay-safe hash generated from the passed typed data hash for 1271 validation. /// @param hash The typed data hash to wrap in a replay-safe hash. /// @return The replay-safe hash, to be used for 1271 signature generation. /// /// @dev Generates a replay-safe hash to wrap a standard typed data hash. This prevents replay attacks by /// enforcing the domain separator, which includes this contract's address and the chainId. This is only /// relevant for 1271 validation because UserOp validation relies on the UO hash and the Entrypoint has /// safeguards. /// /// NOTE: Like in signature-based validation modules, the returned hash should be used to generate signatures, /// but the original hash should be passed to the external-facing function for 1271 validation. function _replaySafeHash(bytes32 hash) internal view returns (bytes32) { return MessageHashUtils.toTypedDataHash({ domainSeparator: _domainSeparator(), structHash: _hashStructReplaySafeHash(hash) }); } function _getSemiModularAccountStorage() internal pure returns (SemiModularAccountStorage storage) { SemiModularAccountStorage storage _storage; assembly ("memory-safe") { _storage.slot := _SEMI_MODULAR_ACCOUNT_STORAGE_SLOT } return _storage; } // Conditionally skip allocation of call buffers. function _validationIsNative(ValidationLookupKey validationLookupKey) internal pure virtual override returns (bool) { return validationLookupKey.eq(FALLBACK_VALIDATION_LOOKUP_KEY); } /// @notice Adds a EIP-712 replay safe hash wrapper to the digest /// @param hash The hash to wrap in a replay-safe hash /// @return The replay-safe hash function _hashStructReplaySafeHash(bytes32 hash) internal pure virtual returns (bytes32) { bytes32 res; assembly ("memory-safe") { mstore(0x00, _REPLAY_SAFE_HASH_TYPEHASH) mstore(0x20, hash) res := keccak256(0, 0x40) } return res; } /// @dev Overrides ModularAccountView. function _isNativeFunction(uint32 selector) internal pure virtual override returns (bool) { return super._isNativeFunction(selector) || selector == uint32(this.updateFallbackSignerData.selector) || selector == uint32(this.getFallbackSignerData.selector); } /// @dev Overrides ModularAccountView. function _isWrappedNativeFunction(uint32 selector) internal pure virtual override returns (bool) { return super._isWrappedNativeFunction(selector) || selector == uint32(this.updateFallbackSignerData.selector); } }
// 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: 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.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) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.20; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS } /** * @dev The signature derives the `address(0)`. */ error ECDSAInvalidSignature(); /** * @dev The signature has an invalid length. */ error ECDSAInvalidSignatureLength(uint256 length); /** * @dev The signature has an S value that is in the upper half order. */ error ECDSAInvalidSignatureS(bytes32 s); /** * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not * return address(0) without also returning an error description. Errors are documented using an enum (error type) * and a bytes32 providing additional information about the error. * * If no error is returned, then the address can be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { unchecked { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); // We do not check for an overflow here since the shift operation results in 0 or 1. uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError, bytes32) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS, s); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature, bytes32(0)); } return (signer, RecoverError.NoError, bytes32(0)); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s); _throwError(error, errorArg); return recovered; } /** * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. */ function _throwError(RecoverError error, bytes32 errorArg) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert ECDSAInvalidSignature(); } else if (error == RecoverError.InvalidSignatureLength) { revert ECDSAInvalidSignatureLength(uint256(errorArg)); } else if (error == RecoverError.InvalidSignatureS) { revert ECDSAInvalidSignatureS(errorArg); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol) pragma solidity ^0.8.20; import {ECDSA} from "./ECDSA.sol"; import {IERC1271} from "../../interfaces/IERC1271.sol"; /** * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like * Argent and Safe Wallet (previously Gnosis Safe). */ library SignatureChecker { /** * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) { (address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature); return (error == ECDSA.RecoverError.NoError && recovered == signer) || isValidERC1271SignatureNow(signer, hash, signature); } /** * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated * against the signer smart contract using ERC1271. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidERC1271SignatureNow( address signer, bytes32 hash, bytes memory signature ) internal view returns (bool) { (bool success, bytes memory result) = signer.staticcall( abi.encodeCall(IERC1271.isValidSignature, (hash, signature)) ); return (success && result.length >= 32 && abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.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 {ModuleEntity} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol"; import {ValidationLookupKey} from "../libraries/ValidationLocatorLib.sol"; // Magic value for the ModuleEntity of the fallback validation for SemiModularAccount. ModuleEntity constant FALLBACK_VALIDATION = ModuleEntity.wrap(bytes24(0)); // Magic value for the validation entity id of the fallback validation for SemiModularAccount. uint32 constant FALLBACK_VALIDATION_ID = uint32(0); // Magic value for the ValidationLookupKey of the fallback validation for SemiModularAccount. ValidationLookupKey constant FALLBACK_VALIDATION_LOOKUP_KEY = ValidationLookupKey.wrap(uint168(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; /// @notice An enum that is prepended to signatures to differentiate between EOA and contract owner signatures. enum SignatureType { EOA, CONTRACT_OWNER }
// 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/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": 10000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": true, "libraries": {} }
Contract ABI
API[{"inputs":[{"internalType":"contract IEntryPoint","name":"_entryPoint","type":"address"},{"internalType":"contract ModularAccount","name":"_accountImpl","type":"address"},{"internalType":"contract SemiModularAccountBytecode","name":"_semiModularImpl","type":"address"},{"internalType":"address","name":"_singleSignerValidationModule","type":"address"},{"internalType":"address","name":"_webAuthnValidationModule","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidAction","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"salt","type":"uint256"}],"name":"ModularAccountDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"salt","type":"uint256"}],"name":"SemiModularAccountDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"ownerX","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"ownerY","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"salt","type":"uint256"}],"name":"WebAuthnModularAccountDeployed","type":"event"},{"inputs":[],"name":"ACCOUNT_IMPL","outputs":[{"internalType":"contract ModularAccount","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ENTRY_POINT","outputs":[{"internalType":"contract IEntryPoint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SEMI_MODULAR_ACCOUNT_IMPL","outputs":[{"internalType":"contract SemiModularAccountBytecode","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SINGLE_SIGNER_VALIDATION_MODULE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WEBAUTHN_VALIDATION_MODULE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"unstakeDelay","type":"uint32"}],"name":"addStake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint32","name":"entityId","type":"uint32"}],"name":"createAccount","outputs":[{"internalType":"contract ModularAccount","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"createSemiModularAccount","outputs":[{"internalType":"contract SemiModularAccountBytecode","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"ownerX","type":"uint256"},{"internalType":"uint256","name":"ownerY","type":"uint256"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint32","name":"entityId","type":"uint32"}],"name":"createWebAuthnAccount","outputs":[{"internalType":"contract ModularAccount","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint32","name":"entityId","type":"uint32"}],"name":"getAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"getAddressSemiModular","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ownerX","type":"uint256"},{"internalType":"uint256","name":"ownerY","type":"uint256"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint32","name":"entityId","type":"uint32"}],"name":"getAddressWebAuthn","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint32","name":"entityId","type":"uint32"}],"name":"getSalt","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"ownerX","type":"uint256"},{"internalType":"uint256","name":"ownerY","type":"uint256"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint32","name":"entityId","type":"uint32"}],"name":"getSaltWebAuthn","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlockStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"withdrawAddress","type":"address"}],"name":"withdrawStake","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
610120346101aa57601f611bdf38819003918201601f19168301916001600160401b038311848410176101af5780849260c0946040528339810103126101aa578051906001600160a01b03821682036101aa5760208101516001600160a01b03811681036101aa5760408201516001600160a01b03811681036101aa57610088606084016101c5565b91610095608085016101c5565b936001600160a01b03906100ab9060a0016101c5565b1694851561019457600180546001600160a01b0319908116909155600080549182168817815560405197916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a360c05260805260a05260e05261010052611a0590816101da8239608051818181610472015281816107cd01528181610fe50152611310015260a05181818161057301528181610bac0152610f02015260c0518181816105ef015281816106dc01528181610a6501526113a4015260e0518181816108b3015261128501526101005181818161015901526110b90152f35b631e4fbdf760e01b600052600060045260246000fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036101aa5756fe6080604052600436101561001257600080fd5b6000803560e01c80630396cb601461134857806303c7e13114611334578063121ee541146112c55780632a7867c2146112a95780632f8aae1d1461123a578063421a21e01461121d5780634659e0e314610f5c5780636aa3406314610e2b578063715018a614610dcc57806379ba509714610cc45780638b4e464e14610ada5780638da5cb5b14610a8957806394430fa514610a1a578063afa63a1014610744578063bb9fe6bf1461068d578063c23a5cea14610597578063c95ff21e14610528578063d7f8ee7714610436578063d9caed1214610291578063e30c39781461023f578063f2fde38b146101805763f96477ae1461010f57600080fd5b3461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b80fd5b503461017d5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d5773ffffffffffffffffffffffffffffffffffffffff6101cd61142f565b6101d56118ab565b16807fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015573ffffffffffffffffffffffffffffffffffffffff8254167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b503461017d5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57806102ca61142f565b60243573ffffffffffffffffffffffffffffffffffffffff8116809103610432576102f36118ab565b8061035057508180809273ffffffffffffffffffffffffffffffffffffffff4791165af161031f61184d565b50156103285780f35b807f90b8ec180000000000000000000000000000000000000000000000000000000060049252fd5b91806103c89260405173ffffffffffffffffffffffffffffffffffffffff60208201927fa9059cbb0000000000000000000000000000000000000000000000000000000084521660248201526044356044820152604481526103b3606482611502565b519082865af16103c161184d565b908361195b565b805190811515918261040a575b50506103df575080f35b7f5274afe7000000000000000000000000000000000000000000000000000000008252600452602490fd5b819250906020918101031261042e576020015180159081150361042e5738806103d5565b8280fd5b5050fd5b503461017d57602061050a61045661044d366114b9565b92919091611653565b309061050573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690604051917fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076604052616009602052601e5268603d3d8160223d3973600a52605f602120916040526000606052565b61193a565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461017d5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57806105d061142f565b6105d86118ab565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690813b156104325773ffffffffffffffffffffffffffffffffffffffff602484928360405195869485937fc23a5cea0000000000000000000000000000000000000000000000000000000085521660048401525af18015610682576106715750f35b8161067b91611502565b61017d5780f35b6040513d84823e3d90fd5b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d576106c46118ab565b8073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b15610741578180916004604051809481937fbb9fe6bf0000000000000000000000000000000000000000000000000000000083525af18015610682576106715750f35b50fd5b503461017d5761075336611457565b6107618183859496956115e3565b928092604051937fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e207660405261600960205273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016601e5268603d3d8160223d3973600a52605f60212060358601523060581b855260ff85538560158601526055852095863b156109fe57505060015b846040528260605215610857575b60208573ffffffffffffffffffffffffffffffffffffffff60405191168152f35b73ffffffffffffffffffffffffffffffffffffffff9063ffffffff84166020860152169283604082015260408152610890606082611502565b73ffffffffffffffffffffffffffffffffffffffff8516926703000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660409290921b6bffffffff00000000000000001691909117670400000000000000177fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000016176040519061094b602083611502565b838252600036813761095b6116a4565b853b156109fa579061099d85939260405195869485947fe919a62300000000000000000000000000000000000000000000000000000000865260048601611737565b038183875af18015610682576109e5575b50507f965f7f47ad3d6fb75196915b245b826100063bf2d9bc0a5e5304a41843baf97f60208095604051908152a338808080610836565b6109f0828092611502565b61017d57806109ae565b8480fd5b909550605f602184f5948561082857633011642583526004601cfd5b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d5773ffffffffffffffffffffffffffffffffffffffff6020915416604051908152f35b503461017d5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57610b1261142f565b602435610b1f8183611572565b91610b29816118fa565b92849060606040519580518091816020858b01920160045afa507fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f360408801527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076602088015261600960165273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166014528060381b6afe61003d3d8160233d39730161ffc2821152601651875260ff8853018086206035523060601b600152816015526055872095863b15610ca157505050506020936001905b60355215610c41575b505073ffffffffffffffffffffffffffffffffffffffff60405191168152f35b73ffffffffffffffffffffffffffffffffffffffff9060405192835216907fd32049610f0cd3babd266cf338d726cc8c34c8ff97356c0f33c13fa59962ac928473ffffffffffffffffffffffffffffffffffffffff851692a33880610c21565b909192955086f5928315610cb757602094610c18565b633011642585526004601cfd5b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d576001543373ffffffffffffffffffffffffffffffffffffffff821603610da0577fffffffffffffffffffffffff0000000000000000000000000000000000000000166001558054337fffffffffffffffffffffffff0000000000000000000000000000000000000000821617825573ffffffffffffffffffffffffffffffffffffffff3391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f118cdaa700000000000000000000000000000000000000000000000000000000815233600452fd5b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57600490610e066118ab565b7f4a7f394f000000000000000000000000000000000000000000000000000000008152fd5b503461017d5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602090610e6661142f565b90610e7c610e7660243584611572565b926118fa565b9160405183519261ffc284113d3d3e805b848110610f48575061050a94507fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f360408301527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e20768683015261600960165273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166014528360381b6961003d3d8160233d397301905260165181526060309301902061193a565b858101870151838201606001528601610e8d565b503461017d57610f6b366114b9565b610f7a81838587959897611653565b9380604051907fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e207660405261600960205273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016601e5268603d3d8160223d3973600a52605f60212060358301523060581b825260ff82538660158301526055822096873b1561120157505060015b81604052826060521561106f575b60208673ffffffffffffffffffffffffffffffffffffffff60405191168152f35b63ffffffff8316602082015283604082015284606082015260608152611096608082611502565b73ffffffffffffffffffffffffffffffffffffffff8616926703000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660409290921b6bffffffff00000000000000001691909117670400000000000000177fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000161760405190611151602083611502565b83825260003681376111616116a4565b853b156109fa57906111a385939260405195869485947fe919a62300000000000000000000000000000000000000000000000000000000865260048601611737565b038183875af18015610682576111ec575b50507f3c550e8add4d9c37ca7c0b494c244828e4600acf1a41a582815d37b6a039557760208096604051908152a4388080808061104e565b6111f7828092611502565b61017d57806111b4565b909650605f602184f5958661104057633011642583526004601cfd5b503461017d57602061050a61045661123436611457565b916115e3565b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461017d5760206112bd61044d366114b9565b604051908152f35b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461017d5760206112bd61123436611457565b5060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d5760043563ffffffff811680910361142b5761138c6118ab565b8173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691823b1561142b5781906024604051809581937f0396cb60000000000000000000000000000000000000000000000000000000008352600483015234905af1801561141e576114105780f35b61141991611502565b388180f35b50604051903d90823e3d90fd5b5080fd5b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361145257565b600080fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126114525760043573ffffffffffffffffffffffffffffffffffffffff8116810361145257906024359060443563ffffffff811681036114525790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126114525760043590602435906044359060643563ffffffff811681036114525790565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761154357604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90604051907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000602083019360601b16835260348201527fffffffff000000000000000000000000000000000000000000000000000000006054820152603881526115dd605882611502565b51902090565b917fffffffff0000000000000000000000000000000000000000000000000000000090604051927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000602085019560601b168552603484015260e01b166054820152603881526115dd605882611502565b927fffffffff00000000000000000000000000000000000000000000000000000000919260405193602085019586526040850152606084015260e01b166080820152606481526115dd608482611502565b604051906116b3602083611502565b600080835282815b8281106116c757505050565b8060606020809385010152016116bb565b919082519283825260005b8481106117225750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b806020809284010151828286010152016116e3565b929190917fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000006080850193168452608060208501528151809352602060a0850192019260005b818110611815575050611797925083820360408501526116d8565b906060818303910152815180825260208201916020808360051b8301019401926000915b8383106117ca57505050505090565b9091929394602080611806837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0866001960301875289516116d8565b970193019301919392906117bb565b84517fffffffff000000000000000000000000000000000000000000000000000000001684526020948501949093019260010161177c565b3d156118a6573d9067ffffffffffffffff8211611543576040519161189a60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611502565b82523d6000602084013e565b606090565b73ffffffffffffffffffffffffffffffffffffffff6000541633036118cc57565b7f118cdaa7000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519160601b16602082015260148152611937603482611502565b90565b91909160ff60005360355260601b6001526015526055600020906000603552565b9061199a575080511561197057805190602001fd5b7f1425ea420000000000000000000000000000000000000000000000000000000060005260046000fd5b815115806119ef575b6119ab575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b50803b156119a356fea164736f6c634300081a000a0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da03200000000000000000000000000000000000002377b26b1eda7b0bc371c60dd4f000000000000000000000000000000000000c5a9089039570dd36455b5c0738300000000000000000000000000000000000099de0bf6fa90deb851e2a2df7d830000000000000000000000000000000000001d9d34e07d9834274df9ae575217000000000000000000000000ddf32240b4ca3184de7ec8f0d5aba27dec8b7a5c
Deployed Bytecode
0x6080604052600436101561001257600080fd5b6000803560e01c80630396cb601461134857806303c7e13114611334578063121ee541146112c55780632a7867c2146112a95780632f8aae1d1461123a578063421a21e01461121d5780634659e0e314610f5c5780636aa3406314610e2b578063715018a614610dcc57806379ba509714610cc45780638b4e464e14610ada5780638da5cb5b14610a8957806394430fa514610a1a578063afa63a1014610744578063bb9fe6bf1461068d578063c23a5cea14610597578063c95ff21e14610528578063d7f8ee7714610436578063d9caed1214610291578063e30c39781461023f578063f2fde38b146101805763f96477ae1461010f57600080fd5b3461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000001d9d34e07d9834274df9ae575217168152f35b80fd5b503461017d5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d5773ffffffffffffffffffffffffffffffffffffffff6101cd61142f565b6101d56118ab565b16807fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015573ffffffffffffffffffffffffffffffffffffffff8254167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b503461017d5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57806102ca61142f565b60243573ffffffffffffffffffffffffffffffffffffffff8116809103610432576102f36118ab565b8061035057508180809273ffffffffffffffffffffffffffffffffffffffff4791165af161031f61184d565b50156103285780f35b807f90b8ec180000000000000000000000000000000000000000000000000000000060049252fd5b91806103c89260405173ffffffffffffffffffffffffffffffffffffffff60208201927fa9059cbb0000000000000000000000000000000000000000000000000000000084521660248201526044356044820152604481526103b3606482611502565b519082865af16103c161184d565b908361195b565b805190811515918261040a575b50506103df575080f35b7f5274afe7000000000000000000000000000000000000000000000000000000008252600452602490fd5b819250906020918101031261042e576020015180159081150361042e5738806103d5565b8280fd5b5050fd5b503461017d57602061050a61045661044d366114b9565b92919091611653565b309061050573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000002377b26b1eda7b0bc371c60dd4f1690604051917fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076604052616009602052601e5268603d3d8160223d3973600a52605f602120916040526000606052565b61193a565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000c5a9089039570dd36455b5c07383168152f35b503461017d5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57806105d061142f565b6105d86118ab565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0321690813b156104325773ffffffffffffffffffffffffffffffffffffffff602484928360405195869485937fc23a5cea0000000000000000000000000000000000000000000000000000000085521660048401525af18015610682576106715750f35b8161067b91611502565b61017d5780f35b6040513d84823e3d90fd5b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d576106c46118ab565b8073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da03216803b15610741578180916004604051809481937fbb9fe6bf0000000000000000000000000000000000000000000000000000000083525af18015610682576106715750f35b50fd5b503461017d5761075336611457565b6107618183859496956115e3565b928092604051937fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e207660405261600960205273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000002377b26b1eda7b0bc371c60dd4f16601e5268603d3d8160223d3973600a52605f60212060358601523060581b855260ff85538560158601526055852095863b156109fe57505060015b846040528260605215610857575b60208573ffffffffffffffffffffffffffffffffffffffff60405191168152f35b73ffffffffffffffffffffffffffffffffffffffff9063ffffffff84166020860152169283604082015260408152610890606082611502565b73ffffffffffffffffffffffffffffffffffffffff8516926703000000000000007f00000000000000000000000000000000000099de0bf6fa90deb851e2a2df7d8360601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660409290921b6bffffffff00000000000000001691909117670400000000000000177fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000016176040519061094b602083611502565b838252600036813761095b6116a4565b853b156109fa579061099d85939260405195869485947fe919a62300000000000000000000000000000000000000000000000000000000865260048601611737565b038183875af18015610682576109e5575b50507f965f7f47ad3d6fb75196915b245b826100063bf2d9bc0a5e5304a41843baf97f60208095604051908152a338808080610836565b6109f0828092611502565b61017d57806109ae565b8480fd5b909550605f602184f5948561082857633011642583526004601cfd5b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032168152f35b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d5773ffffffffffffffffffffffffffffffffffffffff6020915416604051908152f35b503461017d5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57610b1261142f565b602435610b1f8183611572565b91610b29816118fa565b92849060606040519580518091816020858b01920160045afa507fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f360408801527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076602088015261600960165273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000c5a9089039570dd36455b5c07383166014528060381b6afe61003d3d8160233d39730161ffc2821152601651875260ff8853018086206035523060601b600152816015526055872095863b15610ca157505050506020936001905b60355215610c41575b505073ffffffffffffffffffffffffffffffffffffffff60405191168152f35b73ffffffffffffffffffffffffffffffffffffffff9060405192835216907fd32049610f0cd3babd266cf338d726cc8c34c8ff97356c0f33c13fa59962ac928473ffffffffffffffffffffffffffffffffffffffff851692a33880610c21565b909192955086f5928315610cb757602094610c18565b633011642585526004601cfd5b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d576001543373ffffffffffffffffffffffffffffffffffffffff821603610da0577fffffffffffffffffffffffff0000000000000000000000000000000000000000166001558054337fffffffffffffffffffffffff0000000000000000000000000000000000000000821617825573ffffffffffffffffffffffffffffffffffffffff3391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f118cdaa700000000000000000000000000000000000000000000000000000000815233600452fd5b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57600490610e066118ab565b7f4a7f394f000000000000000000000000000000000000000000000000000000008152fd5b503461017d5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602090610e6661142f565b90610e7c610e7660243584611572565b926118fa565b9160405183519261ffc284113d3d3e805b848110610f48575061050a94507fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f360408301527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e20768683015261600960165273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000c5a9089039570dd36455b5c07383166014528360381b6961003d3d8160233d397301905260165181526060309301902061193a565b858101870151838201606001528601610e8d565b503461017d57610f6b366114b9565b610f7a81838587959897611653565b9380604051907fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e207660405261600960205273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000002377b26b1eda7b0bc371c60dd4f16601e5268603d3d8160223d3973600a52605f60212060358301523060581b825260ff82538660158301526055822096873b1561120157505060015b81604052826060521561106f575b60208673ffffffffffffffffffffffffffffffffffffffff60405191168152f35b63ffffffff8316602082015283604082015284606082015260608152611096608082611502565b73ffffffffffffffffffffffffffffffffffffffff8616926703000000000000007f0000000000000000000000000000000000001d9d34e07d9834274df9ae57521760601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660409290921b6bffffffff00000000000000001691909117670400000000000000177fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000161760405190611151602083611502565b83825260003681376111616116a4565b853b156109fa57906111a385939260405195869485947fe919a62300000000000000000000000000000000000000000000000000000000865260048601611737565b038183875af18015610682576111ec575b50507f3c550e8add4d9c37ca7c0b494c244828e4600acf1a41a582815d37b6a039557760208096604051908152a4388080808061104e565b6111f7828092611502565b61017d57806111b4565b909650605f602184f5958661104057633011642583526004601cfd5b503461017d57602061050a61045661123436611457565b916115e3565b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000099de0bf6fa90deb851e2a2df7d83168152f35b503461017d5760206112bd61044d366114b9565b604051908152f35b503461017d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000002377b26b1eda7b0bc371c60dd4f168152f35b503461017d5760206112bd61123436611457565b5060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017d5760043563ffffffff811680910361142b5761138c6118ab565b8173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0321691823b1561142b5781906024604051809581937f0396cb60000000000000000000000000000000000000000000000000000000008352600483015234905af1801561141e576114105780f35b61141991611502565b388180f35b50604051903d90823e3d90fd5b5080fd5b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361145257565b600080fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126114525760043573ffffffffffffffffffffffffffffffffffffffff8116810361145257906024359060443563ffffffff811681036114525790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126114525760043590602435906044359060643563ffffffff811681036114525790565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761154357604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90604051907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000602083019360601b16835260348201527fffffffff000000000000000000000000000000000000000000000000000000006054820152603881526115dd605882611502565b51902090565b917fffffffff0000000000000000000000000000000000000000000000000000000090604051927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000602085019560601b168552603484015260e01b166054820152603881526115dd605882611502565b927fffffffff00000000000000000000000000000000000000000000000000000000919260405193602085019586526040850152606084015260e01b166080820152606481526115dd608482611502565b604051906116b3602083611502565b600080835282815b8281106116c757505050565b8060606020809385010152016116bb565b919082519283825260005b8481106117225750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b806020809284010151828286010152016116e3565b929190917fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000006080850193168452608060208501528151809352602060a0850192019260005b818110611815575050611797925083820360408501526116d8565b906060818303910152815180825260208201916020808360051b8301019401926000915b8383106117ca57505050505090565b9091929394602080611806837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0866001960301875289516116d8565b970193019301919392906117bb565b84517fffffffff000000000000000000000000000000000000000000000000000000001684526020948501949093019260010161177c565b3d156118a6573d9067ffffffffffffffff8211611543576040519161189a60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611502565b82523d6000602084013e565b606090565b73ffffffffffffffffffffffffffffffffffffffff6000541633036118cc57565b7f118cdaa7000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519160601b16602082015260148152611937603482611502565b90565b91909160ff60005360355260601b6001526015526055600020906000603552565b9061199a575080511561197057805190602001fd5b7f1425ea420000000000000000000000000000000000000000000000000000000060005260046000fd5b815115806119ef575b6119ab575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b50803b156119a356fea164736f6c634300081a000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da03200000000000000000000000000000000000002377b26b1eda7b0bc371c60dd4f000000000000000000000000000000000000c5a9089039570dd36455b5c0738300000000000000000000000000000000000099de0bf6fa90deb851e2a2df7d830000000000000000000000000000000000001d9d34e07d9834274df9ae575217000000000000000000000000ddf32240b4ca3184de7ec8f0d5aba27dec8b7a5c
-----Decoded View---------------
Arg [0] : _entryPoint (address): 0x0000000071727De22E5E9d8BAf0edAc6f37da032
Arg [1] : _accountImpl (address): 0x00000000000002377B26b1EdA7b0BC371C60DD4f
Arg [2] : _semiModularImpl (address): 0x000000000000c5A9089039570Dd36455b5C07383
Arg [3] : _singleSignerValidationModule (address): 0x00000000000099DE0BF6fA90dEB851E2A2df7d83
Arg [4] : _webAuthnValidationModule (address): 0x0000000000001D9d34E07D9834274dF9ae575217
Arg [5] : owner (address): 0xDdF32240B4ca3184De7EC8f0D5Aba27dEc8B7A5C
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032
Arg [1] : 00000000000000000000000000000000000002377b26b1eda7b0bc371c60dd4f
Arg [2] : 000000000000000000000000000000000000c5a9089039570dd36455b5c07383
Arg [3] : 00000000000000000000000000000000000099de0bf6fa90deb851e2a2df7d83
Arg [4] : 0000000000000000000000000000000000001d9d34e07d9834274df9ae575217
Arg [5] : 000000000000000000000000ddf32240b4ca3184de7ec8f0d5aba27dec8b7a5c
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.