diff --git a/contracts/smart-account/BaseSmartAccount.sol b/contracts/smart-account/BaseSmartAccount.sol index 05ccb7a08..07c7914ee 100644 --- a/contracts/smart-account/BaseSmartAccount.sol +++ b/contracts/smart-account/BaseSmartAccount.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.17; -import {IAccount} from "@account-abstraction/contracts/interfaces/IAccount.sol"; +import {IBaseSmartAccount} from "./interfaces/IBaseSmartAccount.sol"; import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; import {UserOperationLib, UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; -import {BaseSmartAccountErrors} from "./common/Errors.sol"; import "@account-abstraction/contracts/core/Helpers.sol"; /** @@ -12,59 +11,35 @@ import "@account-abstraction/contracts/core/Helpers.sol"; * This contract provides the basic logic for implementing the IAccount interface: validateUserOp function * Specific account implementation should inherit it and provide the account-specific logic */ -abstract contract BaseSmartAccount is IAccount, BaseSmartAccountErrors { +abstract contract BaseSmartAccount is IBaseSmartAccount { using UserOperationLib for UserOperation; // Return value in case of signature failure, with no time-range. // equivalent to _packValidationData(true,0,0); uint256 internal constant SIG_VALIDATION_FAILED = 1; - /** - * @dev Initialize the Smart Account with required states. - * @param handler Default fallback handler for the Smart Account. - * @param moduleSetupContract Initializes the auth module; can be a factory or registry for multiple accounts. - * @param moduleSetupData Contains address of the Setup Contract and setup data. - * @notice Ensure this is callable only once (use initializer modifier or state checks). - */ + /// @inheritdoc IBaseSmartAccount function init( address handler, address moduleSetupContract, bytes calldata moduleSetupData - ) external virtual returns (address); + ) external virtual override returns (address); - /** - * Validates the userOp. - * @param userOp validate the userOp.signature field - * @param userOpHash convenient field: the hash of the request, to check the signature against - * (also hashes the entrypoint and chain id) - * @param missingAccountFunds the amount of funds required to pay to EntryPoint to pay for the userOp execution. - * @return validationData signature and time-range of this operation - * <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 no time-range in account, return SIG_VALIDATION_FAILED (1) for signature failure. - * Note that the validation code cannot use block.timestamp (or block.number) directly. - */ + /// @inheritdoc IBaseSmartAccount function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds ) external virtual override returns (uint256); - /** - * @return nonce the account nonce. - * @dev This method returns the next sequential nonce. - * @notice Provides 2D nonce functionality by allowing to use a nonce of a specific key. - */ - function nonce(uint192 _key) public view virtual returns (uint256) { + /// @inheritdoc IBaseSmartAccount + function nonce( + uint192 _key + ) public view virtual override returns (uint256) { return entryPoint().getNonce(address(this), _key); } - /** - * return the entryPoint used by this account. - * subclass should return the current entryPoint used by this account. - */ + /// @inheritdoc IBaseSmartAccount function entryPoint() public view virtual returns (IEntryPoint); /** diff --git a/contracts/smart-account/SmartAccount.sol b/contracts/smart-account/SmartAccount.sol index aab2b85ce..ae4dde88e 100644 --- a/contracts/smart-account/SmartAccount.sol +++ b/contracts/smart-account/SmartAccount.sol @@ -1,15 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {BaseSmartAccount, IEntryPoint, UserOperation} from "./BaseSmartAccount.sol"; import {ModuleManager} from "./base/ModuleManager.sol"; import {FallbackManager} from "./base/FallbackManager.sol"; import {LibAddress} from "./libs/LibAddress.sol"; import {ISignatureValidator} from "./interfaces/ISignatureValidator.sol"; -import {IERC165} from "./interfaces/IERC165.sol"; -import {SmartAccountErrors} from "./common/Errors.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IAuthorizationModule} from "./interfaces/IAuthorizationModule.sol"; +import {ISmartAccount} from "./interfaces/ISmartAccount.sol"; +import {IBaseSmartAccount} from "./interfaces/IBaseSmartAccount.sol"; +import {IModuleManager} from "./interfaces/base/IModuleManager.sol"; +import {IFallbackManager} from "./interfaces/base/IFallbackManager.sol"; /** * @title SmartAccount - EIP-4337 compatible smart contract wallet. @@ -26,14 +29,14 @@ contract SmartAccount is ModuleManager, FallbackManager, IERC165, - SmartAccountErrors, + ISmartAccount, ISignatureValidator { using ECDSA for bytes32; using LibAddress for address; // Storage Version - string public constant VERSION = "2.0.0"; + string public constant override VERSION = "2.0.0"; // Owner storage. Deprecated. Left for storage layout compatibility address public ownerDeprecated; @@ -47,16 +50,6 @@ contract SmartAccount is IEntryPoint private immutable ENTRY_POINT; address private immutable SELF; - // Events - event ImplementationUpdated( - address indexed oldImplementation, - address indexed newImplementation - ); - event SmartAccountReceivedNativeToken( - address indexed sender, - uint256 indexed value - ); - /** * @dev Constructor that sets the entry point contract. * _modules[SENTINEL_MODULES] = SENTINEL_MODULES protects implementation from initialization @@ -64,8 +57,9 @@ contract SmartAccount is */ constructor(IEntryPoint anEntryPoint) { SELF = address(this); - if (address(anEntryPoint) == address(0)) + if (address(anEntryPoint) == address(0)) { revert EntryPointCannotBeZero(); + } ENTRY_POINT = anEntryPoint; _modules[SENTINEL_MODULES] = SENTINEL_MODULES; } @@ -81,22 +75,17 @@ contract SmartAccount is emit SmartAccountReceivedNativeToken(msg.sender, msg.value); } - /** - * @dev Initialize the Smart Account with required states - * @param handler Default fallback handler provided in Smart Account - * @param moduleSetupContract Contract, that setups initial auth module for this smart account. - * It can be a module factory or a registry module that serves several smart accounts - * @param moduleSetupData modules setup data (a standard calldata for the module setup contract) - * @notice devs need to make sure it is only callable once by initializer or state check restrictions - * @notice any further implementations that introduces a new state must have a reinit method - * @notice reinitialization is not possible, as _initialSetupModules reverts if the account is already initialized - * which is when there is at least one enabled module - */ + /// @inheritdoc ISmartAccount function init( address handler, address moduleSetupContract, bytes calldata moduleSetupData - ) external virtual override returns (address) { + ) + external + virtual + override(ISmartAccount, BaseSmartAccount) + returns (address) + { if ( _modules[SENTINEL_MODULES] != address(0) || getFallbackHandler() != address(0) @@ -105,41 +94,38 @@ contract SmartAccount is return _initialSetupModules(moduleSetupContract, moduleSetupData); } - /** - * @dev Interface function with the standard name for execute_ncC - * @param dest Address of the contract to call - * @param value Amount of native tokens to send along with the transaction - * @param func Data of the transaction - */ + /// @inheritdoc ISmartAccount function execute( address dest, uint256 value, bytes calldata func - ) external { + ) external override { execute_ncC(dest, value, func); } - /** - * @dev Interface function with the standard name for executeBatch_y6U - * @param dest Addresses of the contracts to call - * @param value Amounts of native tokens to send along with the transactions - * @param func Data of the transactions - */ + /// @inheritdoc ISmartAccount function executeBatch( address[] calldata dest, uint256[] calldata value, bytes[] calldata func - ) external { + ) external override { executeBatch_y6U(dest, value, func); } + /// @inheritdoc IBaseSmartAccount function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds - ) external virtual override returns (uint256 validationData) { - if (msg.sender != address(entryPoint())) + ) + external + virtual + override(IBaseSmartAccount, BaseSmartAccount) + returns (uint256 validationData) + { + if (msg.sender != address(entryPoint())) { revert CallerIsNotAnEntryPoint(msg.sender); + } (, address validationModule) = abi.decode( userOp.signature, @@ -155,47 +141,39 @@ contract SmartAccount is _payPrefund(missingAccountFunds); } - /** - * @dev Adds a module to the allowlist. - * @notice This can only be done via a userOp or a selfcall. - * @notice Enables the module `module` for the wallet. - * @param module Module to be allow-listed. - */ - function enableModule(address module) external virtual override { + /// @inheritdoc IModuleManager + function enableModule( + address module + ) external virtual override(IModuleManager, ModuleManager) { _requireFromEntryPointOrSelf(); _enableModule(module); } - /** - * @dev Setups module for this Smart Account and enables it. - * @notice This can only be done via userOp or a selfcall. - * @notice Enables the module `module` for the wallet. - */ + /// @inheritdoc IModuleManager function setupAndEnableModule( address setupContract, bytes memory setupData - ) external virtual override returns (address) { + ) + external + virtual + override(IModuleManager, ModuleManager) + returns (address) + { _requireFromEntryPointOrSelf(); return _setupAndEnableModule(setupContract, setupData); } - /** - * @dev Sets the fallback handler. - * @notice This can only be done via a UserOp sent by EntryPoint. - * @param handler Handler to be set. - */ + /// @inheritdoc IFallbackManager function setFallbackHandler(address handler) external virtual override { _requireFromEntryPointOrSelf(); _setFallbackHandler(handler); } - /** - * @dev Returns the address of the implementation contract associated with this contract. - * @notice The implementation address is stored in the contract's storage slot with index 0. - */ + /// @inheritdoc ISmartAccount function getImplementation() external view + override returns (address _implementation) { assembly { @@ -203,27 +181,22 @@ contract SmartAccount is } } - /** - * @notice Query if a contract implements an interface - * @param _interfaceId The interface identifier, as specified in ERC165 - * @return `true` if the contract implements `_interfaceID` - */ + /// @inheritdoc IERC165 function supportsInterface( bytes4 _interfaceId ) external view virtual override returns (bool) { return _interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7 } - /** - * @notice All the new implementations MUST have this method! - * @notice Updates the implementation of the base wallet - * @param _implementation New wallet implementation - */ - function updateImplementation(address _implementation) public virtual { + /// @inheritdoc ISmartAccount + function updateImplementation( + address _implementation + ) public virtual override { _requireFromEntryPointOrSelf(); require(_implementation != address(0), "Address cannot be zero"); - if (!_implementation.isContract()) + if (!_implementation.isContract()) { revert InvalidImplementation(_implementation); + } address oldImplementation; assembly { @@ -235,40 +208,35 @@ contract SmartAccount is /* solhint-disable func-name-mixedcase */ - /** - * @dev Execute a transaction (called by entryPoint) - * @notice Name is optimized for this method to be cheaper to be called - * @param dest Address of the contract to call - * @param value Amount of native tokens to send along with the transaction - * @param func Data of the transaction - */ + /// @inheritdoc ISmartAccount function execute_ncC( address dest, uint256 value, bytes calldata func - ) public { + ) public override { _requireFromEntryPoint(); _call(dest, value, func); } - /** - * @dev Execute a sequence of transactions - * @notice Name is optimized for this method to be cheaper to be called - * @param dest Addresses of the contracts to call - * @param value Amounts of native tokens to send along with the transactions - * @param func Data of the transactions - */ + /// @inheritdoc ISmartAccount function executeBatch_y6U( address[] calldata dest, uint256[] calldata value, bytes[] calldata func - ) public { + ) public override { _requireFromEntryPoint(); if ( dest.length == 0 || dest.length != value.length || value.length != func.length - ) revert WrongBatchProvided(dest.length, value.length, func.length, 0); + ) { + revert WrongBatchProvided( + dest.length, + value.length, + func.length, + 0 + ); + } for (uint256 i; i < dest.length; ) { _call(dest[i], value[i], func[i]); unchecked { @@ -279,61 +247,46 @@ contract SmartAccount is /* solhint-enable func-name-mixedcase */ - /** - * @dev Deposit more funds for this account in the entryPoint - */ - function addDeposit() public payable { + /// @inheritdoc ISmartAccount + function addDeposit() public payable override { entryPoint().depositTo{value: msg.value}(address(this)); } - /** - * @dev Withdraw value from the account's deposit - * @param withdrawAddress target to send to - * @param amount to withdraw - */ + /// @inheritdoc ISmartAccount function withdrawDepositTo( address payable withdrawAddress, uint256 amount - ) public payable { + ) public payable override { _requireFromEntryPointOrSelf(); entryPoint().withdrawTo(withdrawAddress, amount); } - /** - * @dev Removes a module from the allowlist. - * @notice This can only be done via a wallet transaction. - * @notice Disables the module `module` for the wallet. - * @param prevModule Module that pointed to the module to be removed in the linked list - * @param module Module to be removed. - */ - function disableModule(address prevModule, address module) public virtual { + /// @inheritdoc IModuleManager + function disableModule( + address prevModule, + address module + ) public virtual override { _requireFromEntryPointOrSelf(); _disableModule(prevModule, module); } - /** - * @dev Returns the current entry point used by this account. - * @return EntryPoint as an `IEntryPoint` interface. - * @dev This function should be implemented by the subclass to return the current entry point used by this account. - */ - function entryPoint() public view virtual override returns (IEntryPoint) { + /// @inheritdoc BaseSmartAccount + function entryPoint() + public + view + virtual + override(IBaseSmartAccount, BaseSmartAccount) + returns (IEntryPoint) + { return ENTRY_POINT; } - /** - * @dev Check current account deposit in the entryPoint - */ - function getDeposit() public view returns (uint256) { + /// @inheritdoc ISmartAccount + function getDeposit() public view override returns (uint256) { return entryPoint().balanceOf(address(this)); } - /** - * Implementation of ISignatureValidator (see `interfaces/ISignatureValidator.sol`) - * @dev Forwards the validation to the module specified in the signature - * @param dataHash 32 bytes hash of the data signed on the behalf of address(msg.sender) - * @param signature Signature byte array associated with dataHash - * @return bytes4 value. - */ + /// @inheritdoc ISignatureValidator function isValidSignature( bytes32 dataHash, bytes memory signature @@ -386,8 +339,11 @@ contract SmartAccount is * within the contract itself only. */ function _requireFromEntryPointOrSelf() internal view { - if (msg.sender != address(entryPoint()) && msg.sender != address(this)) + if ( + msg.sender != address(entryPoint()) && msg.sender != address(this) + ) { revert CallerIsNotEntryPointOrSelf(msg.sender); + } } /** @@ -397,7 +353,8 @@ contract SmartAccount is * within the contract itself only. */ function _requireFromEntryPoint() internal view { - if (msg.sender != address(entryPoint())) + if (msg.sender != address(entryPoint())) { revert CallerIsNotEntryPoint(msg.sender); + } } } diff --git a/contracts/smart-account/base/Executor.sol b/contracts/smart-account/base/Executor.sol index c4abc9494..a8b9d300c 100644 --- a/contracts/smart-account/base/Executor.sol +++ b/contracts/smart-account/base/Executor.sol @@ -1,26 +1,11 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.17; +import {IExecutor} from "../interfaces/base/IExecutor.sol"; import {Enum} from "../common/Enum.sol"; /// @title Executor - A contract that can execute transactions -abstract contract Executor { - // Could add a flag fromEntryPoint for AA txn - event ExecutionFailure( - address indexed to, - uint256 indexed value, - bytes indexed data, - Enum.Operation operation, - uint256 txGas - ); - event ExecutionSuccess( - address indexed to, - uint256 indexed value, - bytes indexed data, - Enum.Operation operation, - uint256 txGas - ); - +abstract contract Executor is IExecutor { function _execute( address to, uint256 value, diff --git a/contracts/smart-account/base/FallbackManager.sol b/contracts/smart-account/base/FallbackManager.sol index adaeb5cb1..883e4edcf 100644 --- a/contracts/smart-account/base/FallbackManager.sol +++ b/contracts/smart-account/base/FallbackManager.sol @@ -2,25 +2,20 @@ pragma solidity 0.8.17; import {SelfAuthorized} from "../common/SelfAuthorized.sol"; -import {FallbackManagerErrors} from "../common/Errors.sol"; +import {IFallbackManager} from "../interfaces/base/IFallbackManager.sol"; /** * @title Fallback Manager - A contract that manages fallback calls made to the Smart Account * @dev Fallback calls are handled by a `handler` contract that is stored at FALLBACK_HANDLER_STORAGE_SLOT * fallback calls are not delegated to the `handler` so they can not directly change Smart Account storage */ -abstract contract FallbackManager is SelfAuthorized, FallbackManagerErrors { +abstract contract FallbackManager is SelfAuthorized, IFallbackManager { // keccak-256 hash of "fallback_manager.handler.address" subtracted by 1 bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d4; uint256[24] private __gap; - event ChangedFallbackHandler( - address indexed previousHandler, - address indexed handler - ); - fallback() external { bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT; @@ -51,12 +46,16 @@ abstract contract FallbackManager is SelfAuthorized, FallbackManagerErrors { } } - /// @dev Allows to add a contract to handle fallback calls. - /// Only fallback calls without value and with data will be forwarded - /// @param handler contract to handle fallback calls. - function setFallbackHandler(address handler) external virtual; + /// @inheritdoc IFallbackManager + function setFallbackHandler(address handler) external virtual override; - function getFallbackHandler() public view returns (address _handler) { + /// @inheritdoc IFallbackManager + function getFallbackHandler() + public + view + override + returns (address _handler) + { assembly { _handler := sload(FALLBACK_HANDLER_STORAGE_SLOT) } diff --git a/contracts/smart-account/base/ModuleManager.sol b/contracts/smart-account/base/ModuleManager.sol index 4a139144c..124c93865 100644 --- a/contracts/smart-account/base/ModuleManager.sol +++ b/contracts/smart-account/base/ModuleManager.sol @@ -3,60 +3,31 @@ pragma solidity 0.8.17; import {SelfAuthorized} from "../common/SelfAuthorized.sol"; import {Executor, Enum} from "./Executor.sol"; -import {ModuleManagerErrors} from "../common/Errors.sol"; +import {IModuleManager} from "../interfaces/base/IModuleManager.sol"; /** * @title Module Manager - A contract that manages modules that can execute transactions * on behalf of the Smart Account via this contract. */ -abstract contract ModuleManager is - SelfAuthorized, - Executor, - ModuleManagerErrors -{ +abstract contract ModuleManager is SelfAuthorized, Executor, IModuleManager { address internal constant SENTINEL_MODULES = address(0x1); mapping(address => address) internal _modules; uint256[24] private __gap; - // Events - event EnabledModule(address module); - event DisabledModule(address module); - event ExecutionFromModuleSuccess(address indexed module); - event ExecutionFromModuleFailure(address indexed module); - event ModuleTransaction( - address module, - address to, - uint256 value, - bytes data, - Enum.Operation operation - ); - - /** - * @dev Adds a module to the allowlist. - * @notice This SHOULD only be done via userOp or a selfcall. - */ - function enableModule(address module) external virtual; + /// @inheritdoc IModuleManager + function enableModule(address module) external virtual override; - /** - * @dev Setups module for this Smart Account and enables it. - * @notice This SHOULD only be done via userOp or a selfcall. - */ + /// @inheritdoc IModuleManager function setupAndEnableModule( address setupContract, bytes memory setupData - ) external virtual returns (address); + ) external virtual override returns (address); - /** - * @dev Returns array of modules. Useful for a widget - * @param start Start of the page. - * @param pageSize Maximum number of modules that should be returned. - * @return array Array of modules. - * @return next Start of the next page. - */ + /// @inheritdoc IModuleManager function getModulesPaginated( address start, uint256 pageSize - ) external view returns (address[] memory array, address next) { + ) external view override returns (address[] memory array, address next) { // Init array with max page size array = new address[](pageSize); @@ -80,20 +51,14 @@ abstract contract ModuleManager is } } - /** - * @dev Allows a Module to execute a Smart Account transaction without any further confirmations. - * @param to Destination address of module transaction. - * @param value Ether value of module transaction. - * @param data Data payload of module transaction. - * @param operation Operation type of module transaction. - */ + /// @inheritdoc IModuleManager function execTransactionFromModule( address to, uint256 value, bytes memory data, Enum.Operation operation, uint256 txGas - ) public virtual returns (bool success) { + ) public virtual override returns (bool success) { // Only whitelisted modules are allowed. if ( msg.sender == SENTINEL_MODULES || _modules[msg.sender] == address(0) @@ -109,28 +74,23 @@ abstract contract ModuleManager is ); } + /// @inheritdoc IModuleManager function execTransactionFromModule( address to, uint256 value, bytes memory data, Enum.Operation operation - ) public virtual returns (bool) { + ) public virtual override returns (bool) { return execTransactionFromModule(to, value, data, operation, 0); } - /** - * @dev Allows a Module to execute a wallet transaction without any further confirmations and returns data - * @param to Destination address of module transaction. - * @param value Ether value of module transaction. - * @param data Data payload of module transaction. - * @param operation Operation type of module transaction. - */ + /// @inheritdoc IModuleManager function execTransactionFromModuleReturnData( address to, uint256 value, bytes memory data, Enum.Operation operation - ) public returns (bool success, bytes memory returnData) { + ) public override returns (bool success, bytes memory returnData) { success = execTransactionFromModule(to, value, data, operation); assembly { @@ -148,31 +108,26 @@ abstract contract ModuleManager is } } - /** - * @dev Allows a Module to execute a batch of Smart Account transactions without any further confirmations. - * @param to Destination address of module transaction. - * @param value Ether value of module transaction. - * @param data Data payload of module transaction. - * @param operations Operation type of module transaction. - */ + /// @inheritdoc IModuleManager function execBatchTransactionFromModule( address[] calldata to, uint256[] calldata value, bytes[] calldata data, Enum.Operation[] calldata operations - ) public virtual returns (bool success) { + ) public virtual override returns (bool success) { if ( to.length == 0 || to.length != value.length || value.length != data.length || data.length != operations.length - ) + ) { revert WrongBatchProvided( to.length, value.length, data.length, operations.length ); + } // Only whitelisted modules are allowed. if ( @@ -193,11 +148,10 @@ abstract contract ModuleManager is } } - /** - * @dev Returns if a module is enabled - * @return True if the module is enabled - */ - function isModuleEnabled(address module) public view returns (bool) { + /// @inheritdoc IModuleManager + function isModuleEnabled( + address module + ) public view override returns (bool) { return SENTINEL_MODULES != module && _modules[module] != address(0); } @@ -209,8 +163,9 @@ abstract contract ModuleManager is */ function _enableModule(address module) internal virtual { // Module address cannot be null or sentinel. - if (module == address(0) || module == SENTINEL_MODULES) + if (module == address(0) || module == SENTINEL_MODULES) { revert ModuleCannotBeZeroOrSentinel(module); + } // Module cannot be added twice. if (_modules[module] != address(0)) revert ModuleAlreadyEnabled(module); @@ -245,14 +200,16 @@ abstract contract ModuleManager is address module ) internal virtual { // Validate module address and check that it corresponds to module index. - if (module == address(0) || module == SENTINEL_MODULES) + if (module == address(0) || module == SENTINEL_MODULES) { revert ModuleCannotBeZeroOrSentinel(module); - if (_modules[prevModule] != module) + } + if (_modules[prevModule] != module) { revert ModuleAndPrevModuleMismatch( module, _modules[prevModule], prevModule ); + } _modules[prevModule] = _modules[module]; delete _modules[module]; emit DisabledModule(module); @@ -270,7 +227,9 @@ abstract contract ModuleManager is if (success) { emit ModuleTransaction(msg.sender, to, value, data, operation); emit ExecutionFromModuleSuccess(msg.sender); - } else emit ExecutionFromModuleFailure(msg.sender); + } else { + emit ExecutionFromModuleFailure(msg.sender); + } } /** @@ -291,7 +250,9 @@ abstract contract ModuleManager is if ( initialAuthorizationModule == address(0) || initialAuthorizationModule == SENTINEL_MODULES - ) revert ModuleCannotBeZeroOrSentinel(initialAuthorizationModule); + ) { + revert ModuleCannotBeZeroOrSentinel(initialAuthorizationModule); + } _modules[initialAuthorizationModule] = SENTINEL_MODULES; _modules[SENTINEL_MODULES] = initialAuthorizationModule; diff --git a/contracts/smart-account/common/Errors.sol b/contracts/smart-account/common/Errors.sol deleted file mode 100644 index c7880b037..000000000 --- a/contracts/smart-account/common/Errors.sol +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.17; - -contract BaseSmartAccountErrors { - /** - * @notice Throws at onlyEntryPoint when msg.sender is not an EntryPoint set for this Smart Account - * @param caller address that tried to call onlyEntryPoint-protected method - */ - error CallerIsNotAnEntryPoint(address caller); -} - -contract FallbackManagerErrors { - /** - * @notice Throws if zero address has been provided as Fallback Handler address - */ - error HandlerCannotBeZero(); -} - -contract ModuleManagerErrors { - /** - * @notice Throws when trying to initialize module manager that already been initialized - */ - error ModulesAlreadyInitialized(); - - /** - * @notice Throws when a delegatecall in course of module manager initialization has failed - */ - error ModulesSetupExecutionFailed(); - - /** - * @notice Throws when address(0) or SENTINEL_MODULES constant has been provided as a module address - * @param module Module address provided - */ - error ModuleCannotBeZeroOrSentinel(address module); - - /** - * @notice Throws when trying to enable module that has already been enabled - * @param module Module address provided - */ - error ModuleAlreadyEnabled(address module); - - /** - * @notice Throws when module and previous module mismatch - * @param expectedModule expected module at modules[prevModule] - * @param returnedModule the module that has been found at modules[prevModule] - * @param prevModule previous module address provided at call - */ - error ModuleAndPrevModuleMismatch( - address expectedModule, - address returnedModule, - address prevModule - ); - - /** - * @notice Throws when trying to execute transaction from module that is not enabled - * @param module Module address provided - */ - error ModuleNotEnabled(address module); - - /** - * @notice Throws when data for executeBatchCall provided in wrong format (i.e. empty array or lengths mismatch) - * @param destLength length of destination contracts array - * @param valueLength length of txn values array - * @param funcLength length of function signatures array - * @param operationLength length of operation types array. 0 if there's no operations - */ - error WrongBatchProvided( - uint256 destLength, - uint256 valueLength, - uint256 funcLength, - uint256 operationLength - ); -} - -contract SmartAccountErrors is BaseSmartAccountErrors, ModuleManagerErrors { - /** - * @notice Throws if zero address has been provided as Entry Point address - */ - error EntryPointCannotBeZero(); - - /** - * @notice Throws at mixedAuth when msg.sender is not an owner neither _self - * @param caller address that tried to call mixedAuth-protected method - */ - error MixedAuthFail(address caller); - - /** - * @notice Throws if trying to change an owner of a SmartAccount to the zero address - */ - error OwnerCannotBeZero(); - - /** - * @notice Throws if zero address has been provided as Base Implementation address - */ - error BaseImplementationCannotBeZero(); - - /** - * @notice Throws if there is no code at implementationAddress - * @param implementationAddress implementation address provided - */ - error InvalidImplementation(address implementationAddress); - - /** - * @notice Throws at onlyOwner when msg.sender is not an owner - * @param caller address that tried to call onlyOwner method - */ - error CallerIsNotOwner(address caller); - - /** - * @notice Throws at _requireFromEntryPointOrOwner when msg.sender is not an EntryPoint neither an owner - * @param caller address that tried to call _requireFromEntryPointOrOwner-protected method - */ - error CallerIsNotEntryPointOrOwner(address caller); - - /** - * @notice Throws at _requireFromEntryPointOrSelf when msg.sender is not an EntryPoint neither self - * @param caller address that tried to call _requireFromEntryPointOrSelf-protected method - */ - error CallerIsNotEntryPointOrSelf(address caller); - - /** - * @notice Throws at _requireFromEntryPoint when msg.sender is not an EntryPoint - * @param caller address that tried to call _requireFromEntryPoint-protected method - */ - error CallerIsNotEntryPoint(address caller); - - /** - * @notice Throws if trying to initialize a Smart Account that has already been initialized - */ - error AlreadyInitialized(); - - /** - * @notice Throws if contract signature is provided in frong format - * @param uintS s converted to uint256 - * @param contractSignatureLength length of a contract signature - * @param signatureLength the whole signature length - */ - error WrongContractSignatureFormat( - uint256 uintS, - uint256 contractSignatureLength, - uint256 signatureLength - ); - - /** - * @notice Throws if isValidSignature for the conrtact signature and data hash differs from EIP1271 Magic Value - * @param contractSignature the contract signature that has been verified - */ - error WrongContractSignature(bytes contractSignature); - - /** - * @notice Throws when if trying to transfer to zero address - */ - error TransferToZeroAddressAttempt(); - - /** - * @notice Throws when module address taken from signature is not enabled - * @param moduleAddressProvided module address taken from signature - */ - error WrongValidationModule(address moduleAddressProvided); - - /** - * @notice Thrown when the function that must be called only via delegatecall is called directly - */ - error DelegateCallsOnly(); - - /** - * @notice Thrown when trying to use address of the Smart Account as an owner for itself - */ - error OwnerCanNotBeSelf(); - - /** - * @notice Thrown when trying to use current owner as a new owner in a _setOwner() call - */ - error OwnerProvidedIsSame(); -} - -contract SmartAccountFactoryErrors is SmartAccountErrors { - /** - * @notice Throws when the new Proxy deployment fails - * @param owner Owner of a Proxy (Smart Account) - * @param index Deployment index - */ - error ProxyDeploymentFailed(address owner, uint256 index); -} - -contract SelfAuthorizedErrors { - /** - * @notice Throws when the caller is not address(this) - * @param caller Caller address - */ - error CallerIsNotSelf(address caller); -} - -contract SingletonPaymasterErrors { - /** - * @notice Throws when the Entrypoint address provided is address(0) - */ - error EntryPointCannotBeZero(); - - /** - * @notice Throws when the verifiying signer address provided is address(0) - */ - error VerifyingSignerCannotBeZero(); - - /** - * @notice Throws when the paymaster address provided is address(0) - */ - error PaymasterIdCannotBeZero(); - - /** - * @notice Throws when the 0 has been provided as deposit - */ - error DepositCanNotBeZero(); - - /** - * @notice Throws when trying to withdraw to address(0) - */ - error CanNotWithdrawToZeroAddress(); - - /** - * @notice Throws when trying to withdraw more than balance available - * @param amountRequired required balance - * @param currentBalance available balance - */ - error InsufficientBalance(uint256 amountRequired, uint256 currentBalance); - - /** - * @notice Throws when signature provided has invalid length - * @param sigLength length oif the signature provided - */ - error InvalidPaymasterSignatureLength(uint256 sigLength); -} - -// diff --git a/contracts/smart-account/common/SelfAuthorized.sol b/contracts/smart-account/common/SelfAuthorized.sol index ed7553e1f..d59056730 100644 --- a/contracts/smart-account/common/SelfAuthorized.sol +++ b/contracts/smart-account/common/SelfAuthorized.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.17; -import {SelfAuthorizedErrors} from "../common/Errors.sol"; +import {ISelfAuthorized} from "../interfaces/common/ISelfAuthorized.sol"; /// @title SelfAuthorized - authorizes current contract to perform actions -contract SelfAuthorized is SelfAuthorizedErrors { +contract SelfAuthorized is ISelfAuthorized { modifier authorized() { // This is a function call as it minimized the bytecode size _requireSelfCall(); diff --git a/contracts/smart-account/common/Stakeable.sol b/contracts/smart-account/common/Stakeable.sol index c306f74af..213f79f1c 100644 --- a/contracts/smart-account/common/Stakeable.sol +++ b/contracts/smart-account/common/Stakeable.sol @@ -3,12 +3,13 @@ pragma solidity 0.8.17; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; +import {IStakeable} from "../interfaces/common/IStakeable.sol"; /** * @title Stakeable Entity * @author Fil Makarov - */ -contract Stakeable is Ownable { +contract Stakeable is Ownable, IStakeable { constructor(address _newOwner) { _transferOwnership(_newOwner); } @@ -16,12 +17,12 @@ contract Stakeable is Ownable { function addStake( address epAddress, uint32 unstakeDelaySec - ) external payable onlyOwner { + ) external payable override onlyOwner { require(epAddress != address(0), "Invalid EP address"); IEntryPoint(epAddress).addStake{value: msg.value}(unstakeDelaySec); } - function unlockStake(address epAddress) external onlyOwner { + function unlockStake(address epAddress) external override onlyOwner { require(epAddress != address(0), "Invalid EP address"); IEntryPoint(epAddress).unlockStake(); } @@ -29,7 +30,7 @@ contract Stakeable is Ownable { function withdrawStake( address epAddress, address payable withdrawAddress - ) external onlyOwner { + ) external override onlyOwner { require(epAddress != address(0), "Invalid EP address"); IEntryPoint(epAddress).withdrawStake(withdrawAddress); } diff --git a/contracts/smart-account/estimation/SmartAccountNoAuth.sol b/contracts/smart-account/estimation/SmartAccountNoAuth.sol deleted file mode 100644 index e7c958185..000000000 --- a/contracts/smart-account/estimation/SmartAccountNoAuth.sol +++ /dev/null @@ -1,443 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import {BaseSmartAccount, IEntryPoint, UserOperation} from "../BaseSmartAccount.sol"; -import {ModuleManager} from "../base/ModuleManager.sol"; -import {FallbackManager} from "../base/FallbackManager.sol"; -import {SignatureDecoder} from "../common/SignatureDecoder.sol"; -import {SecuredTokenTransfer} from "../common/SecuredTokenTransfer.sol"; -import {LibAddress} from "../libs/LibAddress.sol"; -import {ISignatureValidator} from "../interfaces/ISignatureValidator.sol"; -import {IERC165} from "../interfaces/IERC165.sol"; -import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; -import {SmartAccountErrors} from "../common/Errors.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {IAuthorizationModule} from "../interfaces/IAuthorizationModule.sol"; - -/** - * @title SmartAccount - EIP-4337 compatible smart contract wallet. - * @dev This contract is the base for the Smart Account functionality. - * - It provides the functionality to execute both gnosis-style txns and AA (EIP-4337) userOps - * - It allows to receive and manage assets. - * - It is responsible for managing the modules and fallbacks. - * - The Smart Account can be extended with smodules, such as Social Recovery, Session Key and others. - * @author Chirag Titiya - - */ -contract SmartAccountNoAuth is - BaseSmartAccount, - ModuleManager, - FallbackManager, - SignatureDecoder, - SecuredTokenTransfer, - IERC165, - ReentrancyGuard, - SmartAccountErrors, - ISignatureValidator -{ - using ECDSA for bytes32; - using LibAddress for address; - - // Storage Version - string public constant VERSION = "1.0.0"; - - // Domain Seperators keccak256("EIP712Domain(uint256 chainId,address verifyingContract)"); - bytes32 internal constant DOMAIN_SEPARATOR_TYPEHASH = - 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218; - - // solhint-disable-next-line - // keccak256("AccountTx(address to,uint256 value,bytes data,uint8 operation,uint256 targetTxGas,uint256 baseGas,uint256 gasPrice,uint256 tokenGasPriceFactor,address gasToken,address refundReceiver,uint256 nonce)"); - bytes32 internal constant ACCOUNT_TX_TYPEHASH = - 0xda033865d68bf4a40a5a7cb4159a99e33dba8569e65ea3e38222eb12d9e66eee; - - // /!\ Owner storage. Deprecated. Left for storage layout compatibility /!\ - address public ownerDeprecated; - - // changed to 2D nonce below - // @notice there is no _nonce - mapping(uint256 => uint256) public nonces; - - // AA immutable storage - IEntryPoint private immutable ENTRY_POINT; - uint256 private immutable CHAIN_ID; - address private immutable SELF; - - // Events - - event ImplementationUpdated( - address indexed oldImplementation, - address indexed newImplementation - ); - event AccountHandlePayment(bytes32 indexed txHash, uint256 indexed payment); - event SmartAccountReceivedNativeToken( - address indexed sender, - uint256 indexed value - ); - - /** - * @dev Constructor that sets the owner of the contract and the entry point contract. - * _modules[SENTINEL_MODULES] = SENTINEL_MODULES protects implementation from initialization - * @param anEntryPoint The address of the entry point contract. - */ - constructor(IEntryPoint anEntryPoint) { - _modules[SENTINEL_MODULES] = SENTINEL_MODULES; - SELF = address(this); - if (address(anEntryPoint) == address(0)) - revert EntryPointCannotBeZero(); - ENTRY_POINT = anEntryPoint; - CHAIN_ID = block.chainid; - } - - /** - * @dev This function is a special fallback function that is triggered when the contract receives Ether. - * It logs an event indicating the amount of Ether received and the sender's address. - * @notice This function is marked as external and payable, meaning it can be called from external - * sources and accepts Ether as payment. - */ - receive() external payable { - if (address(this) == SELF) revert DelegateCallsOnly(); - emit SmartAccountReceivedNativeToken(msg.sender, msg.value); - } - - /** - * @dev Initialize the Smart Account with required states - * @param handler Default fallback handler provided in Smart Account - * @param moduleSetupContract Initializes the auth module; can be a factory or registry for multiple accounts. - * @param moduleSetupData modules setup data (a standard calldata for the module setup contract) - * @notice devs need to make sure it is only callble once by initiazer or state check restrictions - * @notice any further implementations that introduces a new state must have a reinit method - * @notice reinit is not possible, as _initialSetupModules reverts if the account is already initialized - * which is when there is at least one enabled module - */ - function init( - address handler, - address moduleSetupContract, - bytes calldata moduleSetupData - ) external virtual override returns (address) { - _setFallbackHandler(handler); - return _initialSetupModules(moduleSetupContract, moduleSetupData); - } - - /** - * @dev Interface function with the standard name for execute_ncC - * @param dest Address of the contract to call - * @param value Amount of native tokens to send along with the transaction - * @param func Data of the transaction - */ - function execute( - address dest, - uint256 value, - bytes calldata func - ) external { - execute_ncC(dest, value, func); - } - - /** - * @dev Interface function with the standard name for executeBatch_y6U - * @param dest Addresses of the contracts to call - * @param value Amounts of native tokens to send along with the transactions - * @param func Data of the transactions - */ - function executeBatch( - address[] calldata dest, - uint256[] calldata value, - bytes[] calldata func - ) external { - executeBatch_y6U(dest, value, func); - } - - function validateUserOp( - UserOperation calldata userOp, - bytes32 userOpHash, - uint256 missingAccountFunds - ) external virtual override returns (uint256 validationData) { - if (msg.sender != address(entryPoint())) - revert CallerIsNotAnEntryPoint(msg.sender); - - (, address validationModule) = abi.decode( - userOp.signature, - (bytes, address) - ); - if (address(_modules[validationModule]) != address(0)) { - validationData = IAuthorizationModule(validationModule) - .validateUserOp(userOp, userOpHash); - } else { - revert WrongValidationModule(validationModule); - } - _payPrefund(missingAccountFunds); - } - - /** - * @dev Adds a module to the allowlist. - * @notice This can only be done via a userOp or a selfcall. - * @notice Enables the module `module` for the wallet. - * @param module Module to be allow-listed. - */ - function enableModule(address module) external virtual override { - _requireFromEntryPointOrSelf(); - _enableModule(module); - } - - /** - * @dev Setups module for this Smart Account and enables it. - * @notice This can only be done via userOp or a selfcall. - * @notice Enables the module `module` for the wallet. - */ - function setupAndEnableModule( - address setupContract, - bytes memory setupData - ) external virtual override returns (address) { - _requireFromEntryPointOrSelf(); - return _setupAndEnableModule(setupContract, setupData); - } - - function setFallbackHandler(address handler) external virtual override { - _requireFromEntryPoint(); - _setFallbackHandler(handler); - } - - /** - * @notice Query if a contract implements an interface - * @param _interfaceId The interface identifier, as specified in ERC165 - * @return `true` if the contract implements `_interfaceID` - */ - function supportsInterface( - bytes4 _interfaceId - ) external view virtual override returns (bool) { - return _interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7 - } - - /** - * @dev Returns the address of the implementation contract associated with this contract. - * @notice The implementation address is stored in the contract's storage slot with index 0. - */ - function getImplementation() - external - view - returns (address _implementation) - { - assembly { - _implementation := sload(address()) - } - } - - /** - * @notice All the new implementations MUST have this method! - * @notice Updates the implementation of the base wallet - * @param _implementation New wallet implementation - */ - function updateImplementation(address _implementation) public virtual { - _requireFromEntryPointOrSelf(); - require(_implementation != address(0), "Address cannot be zero"); - if (!_implementation.isContract()) - revert InvalidImplementation(_implementation); - address oldImplementation; - - assembly { - oldImplementation := sload(address()) - sstore(address(), _implementation) - } - emit ImplementationUpdated(oldImplementation, _implementation); - } - - /* solhint-disable func-name-mixedcase */ - - /** - * @dev Execute a transaction (called directly from owner, or by entryPoint) - * @notice Name is optimized for this method to be cheaper to be called - * @param dest Address of the contract to call - * @param value Amount of native tokens to send along with the transaction - * @param func Data of the transaction - */ - function execute_ncC( - address dest, - uint256 value, - bytes calldata func - ) public { - _requireFromEntryPoint(); - _call(dest, value, func); - } - - /** - * @dev Execute a sequence of transactions - * @notice Name is optimized for this method to be cheaper to be called - * @param dest Addresses of the contracts to call - * @param value Amounts of native tokens to send along with the transactions - * @param func Data of the transactions - */ - function executeBatch_y6U( - address[] calldata dest, - uint256[] calldata value, - bytes[] calldata func - ) public { - _requireFromEntryPoint(); - if ( - dest.length == 0 || - dest.length != value.length || - value.length != func.length - ) revert WrongBatchProvided(dest.length, value.length, func.length, 0); - for (uint256 i; i < dest.length; ) { - _call(dest[i], value[i], func[i]); - unchecked { - ++i; - } - } - } - - /* solhint-enable func-name-mixedcase */ - - /** - * @dev Deposit more funds for this account in the entryPoint - */ - function addDeposit() public payable { - entryPoint().depositTo{value: msg.value}(address(this)); - } - - /** - * @dev Withdraw value from the account's deposit - * @param withdrawAddress target to send to - * @param amount to withdraw - */ - function withdrawDepositTo( - address payable withdrawAddress, - uint256 amount - ) public payable { - _requireFromEntryPointOrSelf(); - entryPoint().withdrawTo(withdrawAddress, amount); - } - - /** - * @dev Removes a module from the allowlist. - * @notice This can only be done via a wallet transaction. - * @notice Disables the module `module` for the wallet. - * @param prevModule Module that pointed to the module to be removed in the linked list - * @param module Module to be removed. - */ - function disableModule(address prevModule, address module) public virtual { - _requireFromEntryPointOrSelf(); - _disableModule(prevModule, module); - } - - /** - * @dev Returns the current entry point used by this account. - * @return EntryPoint as an `IEntryPoint` interface. - * @dev This function should be implemented by the subclass to return the current entry point used by this account. - */ - function entryPoint() public view virtual override returns (IEntryPoint) { - return ENTRY_POINT; - } - - /** - * @dev Returns the domain separator for this contract, as defined in the EIP-712 standard. - * @return bytes32 The domain separator hash. - */ - function domainSeparator() public view returns (bytes32) { - return - keccak256( - abi.encode( - DOMAIN_SEPARATOR_TYPEHASH, - block.chainid, - address(this) - ) - ); - } - - /** - * @notice Returns the ID of the chain the contract is currently deployed on. - * @return CHAIN_ID The ID of the current chain as a uint256. - */ - function getChainId() public view returns (uint256) { - return CHAIN_ID; - } - - /** - * @dev returns a value from the nonces 2d mapping - * @param batchId : the key of the user's batch being queried - * @return nonce : the number of transactions made within said batch - */ - function getNonce(uint256 batchId) public view virtual returns (uint256) { - return nonces[batchId]; - } - - /** - * Implementation of ISignatureValidator (see `interfaces/ISignatureValidator.sol`) - * @dev If owner is a smart-contract (other smart contract wallet or module, that controls - * signature verifications - like multisig), forward isValidSignature request to it. - * In case of multisig, _signature can be several concatenated signatures - * If owner is EOA, perform a regular ecrecover. - * @param ethSignedDataHash 32 bytes hash of the data signed on the behalf of address(msg.sender) - * prepended with '\x19Ethereum Signed Message:\n' - * @param signature Signature byte array associated with ethSignedDataHash - * @return bytes4 value. - */ - function isValidSignature( - bytes32 ethSignedDataHash, - bytes memory signature - ) public view override returns (bytes4) { - (bytes memory moduleSignature, address validationModule) = abi.decode( - signature, - (bytes, address) - ); - if (address(_modules[validationModule]) != address(0)) { - return - ISignatureValidator(validationModule).isValidSignature( - ethSignedDataHash, - moduleSignature - ); - } else { - revert WrongValidationModule(validationModule); - } - } - - /** - * @dev Check current account deposit in the entryPoint - */ - function getDeposit() public view returns (uint256) { - return entryPoint().balanceOf(address(this)); - } - - /** - * @dev internal method that fecilitates the extenral calls from SmartAccount - * @dev similar to execute() of Executor.sol - * @param target destination address contract/non-contract - * @param value amount of native tokens - * @param data function singature of destination - */ - function _call(address target, uint256 value, bytes memory data) internal { - assembly { - let success := call( - gas(), - target, - value, - add(data, 0x20), - mload(data), - 0, - 0 - ) - let ptr := mload(0x40) - returndatacopy(ptr, 0, returndatasize()) - if iszero(success) { - revert(ptr, returndatasize()) - } - } - } - - /** - * @dev This function allows the owner or entry point to execute certain actions. - * If the caller is not authorized, the function will revert with an error message. - * @notice This modifier is marked as internal and can only be called within the contract itself. - */ - function _requireFromEntryPointOrSelf() internal view { - if (msg.sender != address(entryPoint()) && msg.sender != address(this)) - revert CallerIsNotEntryPointOrSelf(msg.sender); - } - - /** - * @dev This function allows the owner or entry point to execute certain actions. - * If the caller is not authorized, the function will revert with an error message. - * @notice This modifier is marked as internal and can only be called within the contract itself. - */ - function _requireFromEntryPoint() internal view { - if (msg.sender != address(entryPoint())) - revert CallerIsNotEntryPoint(msg.sender); - } -} diff --git a/contracts/smart-account/factory/SmartAccountFactory.sol b/contracts/smart-account/factory/SmartAccountFactory.sol index c12a06f9a..f9e6e3a3f 100644 --- a/contracts/smart-account/factory/SmartAccountFactory.sol +++ b/contracts/smart-account/factory/SmartAccountFactory.sol @@ -5,6 +5,7 @@ import "../Proxy.sol"; import "../BaseSmartAccount.sol"; import {DefaultCallbackHandler} from "../handler/DefaultCallbackHandler.sol"; import {Stakeable} from "../common/Stakeable.sol"; +import {ISmartAccountFactory} from "../interfaces/factory/ISmartAccountFactory.sol"; /** * @title Smart Account Factory - factory responsible for deploying Smart Accounts using CREATE2 and CREATE @@ -12,20 +13,10 @@ import {Stakeable} from "../common/Stakeable.sol"; * This allows keeping the same address for the same Smart Account owner on various chains via CREATE2 * @author Chirag Titiya - */ -contract SmartAccountFactory is Stakeable { +contract SmartAccountFactory is Stakeable, ISmartAccountFactory { address public immutable basicImplementation; DefaultCallbackHandler public immutable minimalHandler; - event AccountCreation( - address indexed account, - address indexed initialAuthModule, - uint256 indexed index - ); - event AccountCreationWithoutIndex( - address indexed account, - address indexed initialAuthModule - ); - constructor( address _basicImplementation, address _newOwner @@ -38,15 +29,12 @@ contract SmartAccountFactory is Stakeable { minimalHandler = new DefaultCallbackHandler(); } - /** - * @notice Allows to find out account address prior to deployment - * @param index extra salt that allows to deploy more accounts if needed for same EOA (default 0) - */ + /// @inheritdoc ISmartAccountFactory function getAddressForCounterFactualAccount( address moduleSetupContract, bytes calldata moduleSetupData, uint256 index - ) external view returns (address _account) { + ) external view override returns (address _account) { // create initializer data based on init method, _owner and minimalHandler bytes memory initializer = _getInitializer( moduleSetupContract, @@ -65,16 +53,12 @@ contract SmartAccountFactory is Stakeable { _account = address(uint160(uint256(hash))); } - /** - * @notice Deploys account using create2 and points it to basicImplementation - * - * @param index extra salt that allows to deploy more account if needed for same EOA (default 0) - */ + /// @inheritdoc ISmartAccountFactory function deployCounterFactualAccount( address moduleSetupContract, bytes calldata moduleSetupData, uint256 index - ) public returns (address proxy) { + ) public override returns (address proxy) { // create initializer data based on init method and parameters bytes memory initializer = _getInitializer( moduleSetupContract, @@ -123,15 +107,11 @@ contract SmartAccountFactory is Stakeable { emit AccountCreation(proxy, initialAuthorizationModule, index); } - /** - * @notice Deploys account using create and points it to _implementation - - * @return proxy address of the deployed account - */ + /// @inheritdoc ISmartAccountFactory function deployAccount( address moduleSetupContract, bytes calldata moduleSetupData - ) public returns (address proxy) { + ) public override returns (address proxy) { bytes memory deploymentData = abi.encodePacked( type(Proxy).creationCode, uint256(uint160(basicImplementation)) @@ -174,11 +154,8 @@ contract SmartAccountFactory is Stakeable { emit AccountCreationWithoutIndex(proxy, initialAuthorizationModule); } - /** - * @dev Allows to retrieve the creation code used for the Proxy deployment. - * @return The creation code for the Proxy. - */ - function accountCreationCode() public pure returns (bytes memory) { + /// @inheritdoc ISmartAccountFactory + function accountCreationCode() public pure override returns (bytes memory) { return type(Proxy).creationCode; } diff --git a/contracts/smart-account/handler/DefaultCallbackHandler.sol b/contracts/smart-account/handler/DefaultCallbackHandler.sol index 3a30ccb14..27b205372 100644 --- a/contracts/smart-account/handler/DefaultCallbackHandler.sol +++ b/contracts/smart-account/handler/DefaultCallbackHandler.sol @@ -3,20 +3,20 @@ pragma solidity 0.8.17; /* solhint-disable no-empty-blocks */ -import {IERC1155TokenReceiver} from "../interfaces/IERC1155TokenReceiver.sol"; -import {IERC721TokenReceiver} from "../interfaces/IERC721TokenReceiver.sol"; -import {IERC777TokensRecipient} from "../interfaces/IERC777TokensRecipient.sol"; -import {IERC165} from "../interfaces/IERC165.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** @title Default Callback Handler - returns true for known token callbacks * @dev Handles EIP-1271 compliant isValidSignature requests. * @notice inspired by Richard Meissner's implementation */ contract DefaultCallbackHandler is - IERC1155TokenReceiver, - IERC777TokensRecipient, - IERC721TokenReceiver, - IERC165 + IERC165, + IERC1155Receiver, + IERC777Recipient, + IERC721Receiver { string public constant NAME = "Default Callback Handler"; string public constant VERSION = "1.0.0"; @@ -25,9 +25,9 @@ contract DefaultCallbackHandler is bytes4 interfaceId ) external view virtual override returns (bool) { return - interfaceId == type(IERC1155TokenReceiver).interfaceId || - interfaceId == type(IERC721TokenReceiver).interfaceId || - interfaceId == type(IERC777TokensRecipient).interfaceId || + interfaceId == type(IERC1155Receiver).interfaceId || + interfaceId == type(IERC721Receiver).interfaceId || + interfaceId == type(IERC777Recipient).interfaceId || interfaceId == type(IERC165).interfaceId; } @@ -38,7 +38,7 @@ contract DefaultCallbackHandler is uint256, bytes calldata ) external pure override returns (bytes4) { - return IERC1155TokenReceiver.onERC1155Received.selector; + return IERC1155Receiver.onERC1155Received.selector; } function onERC1155BatchReceived( @@ -48,7 +48,7 @@ contract DefaultCallbackHandler is uint256[] calldata, bytes calldata ) external pure override returns (bytes4) { - return IERC1155TokenReceiver.onERC1155BatchReceived.selector; + return IERC1155Receiver.onERC1155BatchReceived.selector; } function onERC721Received( @@ -57,7 +57,7 @@ contract DefaultCallbackHandler is uint256, bytes calldata ) external pure override returns (bytes4) { - return IERC721TokenReceiver.onERC721Received.selector; + return IERC721Receiver.onERC721Received.selector; } function tokensReceived( diff --git a/contracts/smart-account/interfaces/IAuthorizationModule.sol b/contracts/smart-account/interfaces/IAuthorizationModule.sol index b82a68b37..8f3761a88 100644 --- a/contracts/smart-account/interfaces/IAuthorizationModule.sol +++ b/contracts/smart-account/interfaces/IAuthorizationModule.sol @@ -1,9 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; + import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; // interface for modules to verify singatures signed over userOpHash interface IAuthorizationModule { + /** + * @dev validates userOperation. Expects userOp.callData to be an executeBatch + * or executeBatch_y6U call. If something goes wrong, reverts. + * @param userOp User Operation to be validated. + * @param userOpHash Hash of the User Operation to be validated. + * @return validationData SIG_VALIDATION_FAILED or packed validation result. + */ function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash diff --git a/contracts/smart-account/interfaces/IBaseSmartAccount.sol b/contracts/smart-account/interfaces/IBaseSmartAccount.sol new file mode 100644 index 000000000..5c966c77f --- /dev/null +++ b/contracts/smart-account/interfaces/IBaseSmartAccount.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.17; + +import {IAccount} from "@account-abstraction/contracts/interfaces/IAccount.sol"; +import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; +import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; + +/** + * Basic account implementation. + * This contract provides the basic logic for implementing the IAccount interface: validateUserOp function + * Specific account implementation should inherit it and provide the account-specific logic + */ +interface IBaseSmartAccount is IAccount { + /** + * @notice Throws at onlyEntryPoint when msg.sender is not an EntryPoint set for this Smart Account + * @param caller address that tried to call onlyEntryPoint-protected method + */ + error CallerIsNotAnEntryPoint(address caller); + + /** + * @dev Initialize the Smart Account with required states. + * @param handler Default fallback handler for the Smart Account. + * @param moduleSetupContract Initializes the auth module; can be a factory or registry for multiple accounts. + * @param moduleSetupData Contains address of the Setup Contract and setup data. + * @notice Ensure this is callable only once (use initializer modifier or state checks). + */ + function init( + address handler, + address moduleSetupContract, + bytes calldata moduleSetupData + ) external returns (address); + + /** + * Validates the userOp. + * @param userOp validate the userOp.signature field + * @param userOpHash convenient field: the hash of the request, to check the signature against + * (also hashes the entrypoint and chain id) + * @param missingAccountFunds the amount of funds required to pay to EntryPoint to pay for the userOp execution. + * @return validationData signature and time-range of this operation + * <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 no time-range in account, return SIG_VALIDATION_FAILED (1) for signature failure. + * Note that the validation code cannot use block.timestamp (or block.number) directly. + */ + function validateUserOp( + UserOperation calldata userOp, + bytes32 userOpHash, + uint256 missingAccountFunds + ) external returns (uint256); + + /** + * @return nonce the account nonce. + * @dev This method returns the next sequential nonce. + * @notice Provides 2D nonce functionality by allowing to use a nonce of a specific key. + */ + function nonce(uint192 _key) external view returns (uint256); + + /** + * return the entryPoint used by this account. + * subclass should return the current entryPoint used by this account. + */ + function entryPoint() external view returns (IEntryPoint); +} diff --git a/contracts/smart-account/interfaces/IERC1155TokenReceiver.sol b/contracts/smart-account/interfaces/IERC1155TokenReceiver.sol deleted file mode 100644 index 50beca5ee..000000000 --- a/contracts/smart-account/interfaces/IERC1155TokenReceiver.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.17; - -/** - Note: The ERC-165 identifier for this interface is 0x4e2312e0. -*/ -interface IERC1155TokenReceiver { - /** - * @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 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 allowed - */ - function onERC1155BatchReceived( - address _operator, - address _from, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data - ) external returns (bytes4); -} diff --git a/contracts/smart-account/interfaces/IERC165.sol b/contracts/smart-account/interfaces/IERC165.sol deleted file mode 100644 index 7c875dbe5..000000000 --- a/contracts/smart-account/interfaces/IERC165.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.17; - -/** - * @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); -} diff --git a/contracts/smart-account/interfaces/IERC721TokenReceiver.sol b/contracts/smart-account/interfaces/IERC721TokenReceiver.sol deleted file mode 100644 index 15721834e..000000000 --- a/contracts/smart-account/interfaces/IERC721TokenReceiver.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.17; - -/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02. -interface IERC721TokenReceiver { - /// @notice Handle the receipt of an NFT - /// @dev The ERC721 smart contract calls this function on the recipient - /// after a `transfer`. This function MAY throw to revert and reject the - /// transfer. Return of other than the magic value MUST result in the - /// transaction being reverted. - /// Note: the contract address is always the message sender. - /// @param _operator The address which called `safeTransferFrom` function - /// @param _from The address which previously owned the token - /// @param _tokenId The NFT identifier which is being transferred - /// @param _data Additional data with no specified format - /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` - /// unless throwing - function onERC721Received( - address _operator, - address _from, - uint256 _tokenId, - bytes calldata _data - ) external returns (bytes4); -} diff --git a/contracts/smart-account/interfaces/IERC777TokensRecipient.sol b/contracts/smart-account/interfaces/IERC777TokensRecipient.sol deleted file mode 100644 index 6497c3817..000000000 --- a/contracts/smart-account/interfaces/IERC777TokensRecipient.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.17; - -interface IERC777TokensRecipient { - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata data, - bytes calldata operatorData - ) external; -} diff --git a/contracts/smart-account/interfaces/ISessionKeyManager.sol b/contracts/smart-account/interfaces/ISessionKeyManager.sol deleted file mode 100644 index e624915fe..000000000 --- a/contracts/smart-account/interfaces/ISessionKeyManager.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -interface ISessionKeyManager { - /** - * @dev validates that Session Key + parameters are enabled - * by being included into the merkle tree - * @param userOpSender smartAccount for which session key is being validated - * @param validUntil timestamp when the session key expires - * @param validAfter timestamp when the session key becomes valid - * @param sessionValidationModule address of the Session Validation Module - * @param sessionKeyData session parameters (limitations/permissions) - * @param merkleProof merkle proof for the leaf which represents this session key + params - * @dev if doesn't revert, session key is considered valid - */ - function validateSessionKey( - address userOpSender, - uint48 validUntil, - uint48 validAfter, - address sessionValidationModule, - bytes calldata sessionKeyData, - bytes32[] calldata merkleProof - ) external; -} diff --git a/contracts/smart-account/interfaces/ISignatureValidator.sol b/contracts/smart-account/interfaces/ISignatureValidator.sol index f5b03c74e..16637c81b 100644 --- a/contracts/smart-account/interfaces/ISignatureValidator.sol +++ b/contracts/smart-account/interfaces/ISignatureValidator.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.17; -contract ISignatureValidatorConstants { - // bytes4(keccak256("isValidSignature(bytes32,bytes)") - bytes4 internal constant EIP1271_MAGIC_VALUE = 0x1626ba7e; -} +// bytes4(keccak256("isValidSignature(bytes32,bytes)") +bytes4 constant EIP1271_MAGIC_VALUE = 0x1626ba7e; -abstract contract ISignatureValidator is ISignatureValidatorConstants { +interface ISignatureValidator { /** * @dev Should return whether the signature provided is valid for the provided data * @param _dataHash Arbitrary length data signed on behalf of address(this) @@ -19,5 +17,5 @@ abstract contract ISignatureValidator is ISignatureValidatorConstants { function isValidSignature( bytes32 _dataHash, bytes memory _signature - ) public view virtual returns (bytes4); + ) external view returns (bytes4); } diff --git a/contracts/smart-account/interfaces/ISmartAccount.sol b/contracts/smart-account/interfaces/ISmartAccount.sol new file mode 100644 index 000000000..a7d2c79da --- /dev/null +++ b/contracts/smart-account/interfaces/ISmartAccount.sol @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.17; + +import {IBaseSmartAccount} from "./IBaseSmartAccount.sol"; +import {IModuleManager} from "./base/IModuleManager.sol"; + +/* solhint-disable func-name-mixedcase */ + +interface ISmartAccount is IBaseSmartAccount, IModuleManager { + // Events + event ImplementationUpdated( + address indexed oldImplementation, + address indexed newImplementation + ); + event SmartAccountReceivedNativeToken( + address indexed sender, + uint256 indexed value + ); + + // Error + + /** + * @notice Throws if zero address has been provided as Entry Point address + */ + + error EntryPointCannotBeZero(); + + /** + * @notice Throws at mixedAuth when msg.sender is not an owner neither _self + * @param caller address that tried to call mixedAuth-protected method + */ + error MixedAuthFail(address caller); + + /** + * @notice Throws if trying to change an owner of a SmartAccount to the zero address + */ + error OwnerCannotBeZero(); + + /** + * @notice Throws if zero address has been provided as Base Implementation address + */ + error BaseImplementationCannotBeZero(); + + /** + * @notice Throws if there is no code at implementationAddress + * @param implementationAddress implementation address provided + */ + error InvalidImplementation(address implementationAddress); + + /** + * @notice Throws at onlyOwner when msg.sender is not an owner + * @param caller address that tried to call onlyOwner method + */ + error CallerIsNotOwner(address caller); + + /** + * @notice Throws at _requireFromEntryPointOrOwner when msg.sender is not an EntryPoint neither an owner + * @param caller address that tried to call _requireFromEntryPointOrOwner-protected method + */ + error CallerIsNotEntryPointOrOwner(address caller); + + /** + * @notice Throws at _requireFromEntryPointOrSelf when msg.sender is not an EntryPoint neither self + * @param caller address that tried to call _requireFromEntryPointOrSelf-protected method + */ + error CallerIsNotEntryPointOrSelf(address caller); + + /** + * @notice Throws at _requireFromEntryPoint when msg.sender is not an EntryPoint + * @param caller address that tried to call _requireFromEntryPoint-protected method + */ + error CallerIsNotEntryPoint(address caller); + + /** + * @notice Throws if trying to initialize a Smart Account that has already been initialized + */ + error AlreadyInitialized(); + + /** + * @notice Throws if contract signature is provided in frong format + * @param uintS s converted to uint256 + * @param contractSignatureLength length of a contract signature + * @param signatureLength the whole signature length + */ + error WrongContractSignatureFormat( + uint256 uintS, + uint256 contractSignatureLength, + uint256 signatureLength + ); + + /** + * @notice Throws if isValidSignature for the conrtact signature and data hash differs from EIP1271 Magic Value + * @param contractSignature the contract signature that has been verified + */ + error WrongContractSignature(bytes contractSignature); + + /** + * @notice Throws when if trying to transfer to zero address + */ + error TransferToZeroAddressAttempt(); + + /** + * @notice Throws when module address taken from signature is not enabled + * @param moduleAddressProvided module address taken from signature + */ + error WrongValidationModule(address moduleAddressProvided); + + /** + * @notice Thrown when the function that must be called only via delegatecall is called directly + */ + error DelegateCallsOnly(); + + /** + * @notice Thrown when trying to use address of the Smart Account as an owner for itself + */ + error OwnerCanNotBeSelf(); + + /** + * @notice Thrown when trying to use current owner as a new owner in a _setOwner() call + */ + error OwnerProvidedIsSame(); + + // Functions + + /** + * @dev Initialize the Smart Account with required states + * @param handler Default fallback handler provided in Smart Account + * @param moduleSetupContract Contract, that setups initial auth module for this smart account. + * It can be a module factory or a registry module that serves several smart accounts + * @param moduleSetupData modules setup data (a standard calldata for the module setup contract) + * @notice devs need to make sure it is only callable once by initializer or state check restrictions + * @notice any further implementations that introduces a new state must have a reinit method + * @notice reinitialization is not possible, as _initialSetupModules reverts if the account is already initialized + * which is when there is at least one enabled module + */ + function init( + address handler, + address moduleSetupContract, + bytes calldata moduleSetupData + ) external returns (address); + + /** + * @dev Interface function with the standard name for execute_ncC + * @param dest Address of the contract to call + * @param value Amount of native tokens to send along with the transaction + * @param func Data of the transaction + */ + function execute(address dest, uint256 value, bytes calldata func) external; + + /** + * @dev Execute a transaction (called by entryPoint) + * @notice Name is optimized for this method to be cheaper to be called + * @param dest Address of the contract to call + * @param value Amount of native tokens to send along with the transaction + * @param func Data of the transaction + */ + function execute_ncC( + address dest, + uint256 value, + bytes calldata func + ) external; + + /** + * @dev Interface function with the standard name for executeBatch_y6U + * @param dest Addresses of the contracts to call + * @param value Amounts of native tokens to send along with the transactions + * @param func Data of the transactions + */ + function executeBatch( + address[] calldata dest, + uint256[] calldata value, + bytes[] calldata func + ) external; + + /** + * @dev Execute a sequence of transactions + * @notice Name is optimized for this method to be cheaper to be called + * @param dest Addresses of the contracts to call + * @param value Amounts of native tokens to send along with the transactions + * @param func Data of the transactions + */ + function executeBatch_y6U( + address[] calldata dest, + uint256[] calldata value, + bytes[] calldata func + ) external; + + /** + * @notice All the new implementations MUST have this method! + * @notice Updates the implementation of the base wallet + * @param _implementation New wallet implementation + */ + function updateImplementation(address _implementation) external; + + /** + * @dev Deposit more funds for this account in the entryPoint + */ + function addDeposit() external payable; + + /** + * @dev Withdraw value from the account's deposit + * @param withdrawAddress target to send to + * @param amount to withdraw + */ + function withdrawDepositTo( + address payable withdrawAddress, + uint256 amount + ) external payable; + + /** + * @dev Check current account deposit in the entryPoint + */ + function getDeposit() external view returns (uint256); + + /** + * @dev Returns the address of the implementation contract associated with this contract. + * @notice The implementation address is stored in the contract's storage slot with index 0. + * @return _implementation implementation address + */ + function getImplementation() + external + view + returns (address _implementation); + + function VERSION() external pure returns (string memory); +} diff --git a/contracts/smart-account/interfaces/base/IExecutor.sol b/contracts/smart-account/interfaces/base/IExecutor.sol new file mode 100644 index 000000000..976a60984 --- /dev/null +++ b/contracts/smart-account/interfaces/base/IExecutor.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.17; + +import {Enum} from "../../common/Enum.sol"; + +/// @title Executor - A contract that can execute transactions +interface IExecutor { + // Could add a flag fromEntryPoint for AA txn + event ExecutionFailure( + address indexed to, + uint256 indexed value, + bytes indexed data, + Enum.Operation operation, + uint256 txGas + ); + event ExecutionSuccess( + address indexed to, + uint256 indexed value, + bytes indexed data, + Enum.Operation operation, + uint256 txGas + ); +} diff --git a/contracts/smart-account/interfaces/base/IFallbackManager.sol b/contracts/smart-account/interfaces/base/IFallbackManager.sol new file mode 100644 index 000000000..6a0f32e99 --- /dev/null +++ b/contracts/smart-account/interfaces/base/IFallbackManager.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.17; + +/** + * @title Fallback Manager - A contract that manages fallback calls made to the Smart Account + * @dev Fallback calls are handled by a `handler` contract that is stored at FALLBACK_HANDLER_STORAGE_SLOT + * fallback calls are not delegated to the `handler` so they can not directly change Smart Account storage + */ +interface IFallbackManager { + // Events + event ChangedFallbackHandler( + address indexed previousHandler, + address indexed handler + ); + + /** + * @notice Throws if zero address has been provided as Fallback Handler address + */ + error HandlerCannotBeZero(); + + /// @dev Allows to add a contract to handle fallback calls. + /// Only fallback calls without value and with data will be forwarded + /// @param handler contract to handle fallback calls. + function setFallbackHandler(address handler) external; + + /// @dev Returns the address of the fallback handler + /// @return _handler address of the fallback handler + function getFallbackHandler() external view returns (address _handler); +} diff --git a/contracts/smart-account/interfaces/base/IModuleManager.sol b/contracts/smart-account/interfaces/base/IModuleManager.sol new file mode 100644 index 000000000..0f989efcb --- /dev/null +++ b/contracts/smart-account/interfaces/base/IModuleManager.sol @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.17; + +import {Enum} from "../../common/Enum.sol"; + +/** + * @title Fallback Manager - A contract that manages fallback calls made to the Smart Account + * @dev Fallback calls are handled by a `handler` contract that is stored at FALLBACK_HANDLER_STORAGE_SLOT + * fallback calls are not delegated to the `handler` so they can not directly change Smart Account storage + */ +interface IModuleManager { + // Events + event EnabledModule(address module); + event DisabledModule(address module); + event ExecutionFromModuleSuccess(address indexed module); + event ExecutionFromModuleFailure(address indexed module); + event ModuleTransaction( + address module, + address to, + uint256 value, + bytes data, + Enum.Operation operation + ); + + /** + * @notice Throws when trying to initialize module manager that already been initialized + */ + error ModulesAlreadyInitialized(); + + /** + * @notice Throws when a delegatecall in course of module manager initialization has failed + */ + error ModulesSetupExecutionFailed(); + + /** + * @notice Throws when address(0) or SENTINEL_MODULES constant has been provided as a module address + * @param module Module address provided + */ + error ModuleCannotBeZeroOrSentinel(address module); + + /** + * @notice Throws when trying to enable module that has already been enabled + * @param module Module address provided + */ + error ModuleAlreadyEnabled(address module); + + /** + * @notice Throws when module and previous module mismatch + * @param expectedModule expected module at modules[prevModule] + * @param returnedModule the module that has been found at modules[prevModule] + * @param prevModule previous module address provided at call + */ + error ModuleAndPrevModuleMismatch( + address expectedModule, + address returnedModule, + address prevModule + ); + + /** + * @notice Throws when trying to execute transaction from module that is not enabled + * @param module Module address provided + */ + error ModuleNotEnabled(address module); + + /** + * @notice Throws when data for executeBatchCall provided in wrong format (i.e. empty array or lengths mismatch) + * @param destLength length of destination contracts array + * @param valueLength length of txn values array + * @param funcLength length of function signatures array + * @param operationLength length of operation types array. 0 if there's no operations + */ + error WrongBatchProvided( + uint256 destLength, + uint256 valueLength, + uint256 funcLength, + uint256 operationLength + ); + + /** + * @dev Adds a module to the allowlist. + * @notice This SHOULD only be done via userOp or a selfcall. + */ + function enableModule(address module) external; + + /** + * @dev Removes a module from the allowlist. + * @notice This can only be done via a wallet transaction. + * @notice Disables the module `module` for the wallet. + * @param prevModule Module that pointed to the module to be removed in the linked list + * @param module Module to be removed. + */ + function disableModule(address prevModule, address module) external; + + /** + * @dev Setups module for this Smart Account and enables it. + * @notice This SHOULD only be done via userOp or a selfcall. + * @notice Enables the module `module` for the wallet. + */ + function setupAndEnableModule( + address setupContract, + bytes memory setupData + ) external returns (address); + + /** + * @dev Allows a Module to execute a Smart Account transaction without any further confirmations. + * @param to Destination address of module transaction. + * @param value Ether value of module transaction. + * @param data Data payload of module transaction. + * @param operation Operation type of module transaction. + */ + function execTransactionFromModule( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation, + uint256 txGas + ) external returns (bool success); + + function execTransactionFromModule( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation + ) external returns (bool); + + /** + * @dev Allows a Module to execute a wallet transaction without any further confirmations and returns data + * @param to Destination address of module transaction. + * @param value Ether value of module transaction. + * @param data Data payload of module transaction. + * @param operation Operation type of module transaction. + */ + function execTransactionFromModuleReturnData( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation + ) external returns (bool success, bytes memory returnData); + + /** + * @dev Allows a Module to execute a batch of Smart Account transactions without any further confirmations. + * @param to Destination address of module transaction. + * @param value Ether value of module transaction. + * @param data Data payload of module transaction. + * @param operations Operation type of module transaction. + */ + function execBatchTransactionFromModule( + address[] calldata to, + uint256[] calldata value, + bytes[] calldata data, + Enum.Operation[] calldata operations + ) external returns (bool success); + + /** + * @dev Returns if a module is enabled + * @return True if the module is enabled + */ + function isModuleEnabled(address module) external view returns (bool); + + /** + * @dev Returns array of modules. Useful for a widget + * @param start Start of the page. + * @param pageSize Maximum number of modules that should be returned. + * @return array Array of modules. + * @return next Start of the next page. + */ + function getModulesPaginated( + address start, + uint256 pageSize + ) external view returns (address[] memory array, address next); +} diff --git a/contracts/smart-account/interfaces/common/ISelfAuthorized.sol b/contracts/smart-account/interfaces/common/ISelfAuthorized.sol new file mode 100644 index 000000000..815fa936f --- /dev/null +++ b/contracts/smart-account/interfaces/common/ISelfAuthorized.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.17; + +/// @title SelfAuthorized - authorizes current contract to perform actions +interface ISelfAuthorized { + /** + * @notice Throws when the caller is not address(this) + * @param caller Caller address + */ + error CallerIsNotSelf(address caller); +} diff --git a/contracts/smart-account/interfaces/common/IStakeable.sol b/contracts/smart-account/interfaces/common/IStakeable.sol new file mode 100644 index 000000000..30ce792bb --- /dev/null +++ b/contracts/smart-account/interfaces/common/IStakeable.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.17; + +/** + * @title Stakeable Entity + * @author Fil Makarov - + */ +interface IStakeable { + function addStake( + address epAddress, + uint32 unstakeDelaySec + ) external payable; + + function unlockStake(address epAddress) external; + + function withdrawStake( + address epAddress, + address payable withdrawAddress + ) external; +} diff --git a/contracts/smart-account/interfaces/factory/ISmartAccountFactory.sol b/contracts/smart-account/interfaces/factory/ISmartAccountFactory.sol new file mode 100644 index 000000000..afda7bbb3 --- /dev/null +++ b/contracts/smart-account/interfaces/factory/ISmartAccountFactory.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity 0.8.17; + +/** + * @title Smart Account Factory - factory responsible for deploying Smart Accounts using CREATE2 and CREATE + * @dev It deploys Smart Accounts as proxies pointing to `basicImplementation` that is immutable. + * This allows keeping the same address for the same Smart Account owner on various chains via CREATE2 + * @author Chirag Titiya - + */ +interface ISmartAccountFactory { + // Events + event AccountCreation( + address indexed account, + address indexed initialAuthModule, + uint256 indexed index + ); + event AccountCreationWithoutIndex( + address indexed account, + address indexed initialAuthModule + ); + + /** + * @notice Deploys account using create2 and points it to basicImplementation + * @param moduleSetupContract address of the module setup contract + * @param moduleSetupData data for module setup contract + * @param index extra salt that allows to deploy more account if needed for same EOA (default 0) + * @return proxy address of the deployed account + */ + function deployCounterFactualAccount( + address moduleSetupContract, + bytes calldata moduleSetupData, + uint256 index + ) external returns (address proxy); + + /** + * @notice Deploys account using create and points it to _implementation + * @param moduleSetupContract address of the module setup contract + * @param moduleSetupData data for module setup contract + * @return proxy address of the deployed account + */ + function deployAccount( + address moduleSetupContract, + bytes calldata moduleSetupData + ) external returns (address proxy); + + /** + * @notice Allows to find out account address prior to deployment + * @param moduleSetupContract address of the module setup contract + * @param moduleSetupData data for module setup contract + * @param index extra salt that allows to deploy more accounts if needed for same EOA (default 0) + * @return _account address of the account + */ + function getAddressForCounterFactualAccount( + address moduleSetupContract, + bytes calldata moduleSetupData, + uint256 index + ) external view returns (address _account); + + /** + * @dev Allows to retrieve the creation code used for the Proxy deployment. + * @return The creation code for the Proxy. + */ + function accountCreationCode() external pure returns (bytes memory); +} diff --git a/contracts/smart-account/interfaces/modules/IBaseAuthorizationModule.sol b/contracts/smart-account/interfaces/modules/IBaseAuthorizationModule.sol new file mode 100644 index 000000000..527927998 --- /dev/null +++ b/contracts/smart-account/interfaces/modules/IBaseAuthorizationModule.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {IAuthorizationModule} from "../../interfaces/IAuthorizationModule.sol"; +import {ISignatureValidator} from "../../interfaces/ISignatureValidator.sol"; + +/* solhint-disable no-empty-blocks */ +interface IBaseAuthorizationModule is + IAuthorizationModule, + ISignatureValidator +{ + +} diff --git a/contracts/smart-account/interfaces/modules/IBatchedSessionRouterModule.sol b/contracts/smart-account/interfaces/modules/IBatchedSessionRouterModule.sol new file mode 100644 index 000000000..1227b85ea --- /dev/null +++ b/contracts/smart-account/interfaces/modules/IBatchedSessionRouterModule.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +/** + * @title Batched Session Router + * @dev Built to process executeBatch and executeBatch_y6U calls + * - Every call inside batch should be covered by an appropriate Session Validation Module + * - Parses data provided and sequentially + * a) verifies the session key was enabled via SessionKeyManager + * b) verifies the session key permissions via Session Validation Modules + * - Should be used with carefully verified and audited Session Validation Modules only + * - Compatible with Biconomy Modular Interface v 0.1 + * @author Fil Makarov - + */ +interface IBatchedSessionRouterModule { + struct SessionData { + uint48 validUntil; + uint48 validAfter; + address sessionValidationModule; + bytes sessionKeyData; + bytes32[] merkleProof; + bytes callSpecificData; + } +} diff --git a/contracts/smart-account/interfaces/modules/IEcdsaOwnershipRegistryModule.sol b/contracts/smart-account/interfaces/modules/IEcdsaOwnershipRegistryModule.sol new file mode 100644 index 000000000..ea13df6c1 --- /dev/null +++ b/contracts/smart-account/interfaces/modules/IEcdsaOwnershipRegistryModule.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +/** + * @title ECDSA ownership Authorization module for Biconomy Smart Accounts. + * @dev Compatible with Biconomy Modular Interface v 0.1 + * - It allows to validate user operations signed by EOA private key. + * - EIP-1271 compatible (ensures Smart Account can validate signed messages). + * - One owner per Smart Account. + * - Does not support outdated eth_sign flow for cheaper validations + * (see https://support.metamask.io/hc/en-us/articles/14764161421467-What-is-eth-sign-and-why-is-it-a-risk-) + * !!!!!!! Only EOA owners supported, no Smart Account Owners + * For Smart Contract Owners check SmartContractOwnership module instead + * @author Fil Makarov - + */ +interface IEcdsaOwnershipRegistryModule { + event OwnershipTransferred( + address indexed smartAccount, + address indexed oldOwner, + address indexed newOwner + ); + + error NoOwnerRegisteredForSmartAccount(address smartAccount); + error AlreadyInitedForSmartAccount(address smartAccount); + error WrongSignatureLength(); + error NotEOA(address account); + error ZeroAddressNotAllowedAsOwner(); + + /** + * @dev Initializes the module for a Smart Account. + * Should be used at a time of first enabling the module for a Smart Account. + * @param eoaOwner The owner of the Smart Account. Should be EOA! + */ + function initForSmartAccount(address eoaOwner) external returns (address); + + /** + * @dev Sets/changes an for a Smart Account. + * Should be called by Smart Account itself. + * @param owner The owner of the Smart Account. + */ + function transferOwnership(address owner) external; + + /** + * @dev Renounces ownership + * should be called by Smart Account. + */ + function renounceOwnership() external; + + /** + * @dev Returns the owner of the Smart Account. Reverts for Smart Accounts without owners. + * @param smartAccount Smart Account address. + * @return owner The owner of the Smart Account. + */ + function getOwner(address smartAccount) external view returns (address); + + /** + * @dev Validates a signature for a message signed by address. + * @dev Also try dataHash.toEthSignedMessageHash() + * @param dataHash hash of the data + * @param moduleSignature Signature to be validated. + * @param smartAccount expected signer Smart Account address. + * @return EIP1271_MAGIC_VALUE if signature is valid, 0xffffffff otherwise. + */ + function isValidSignatureForAddress( + bytes32 dataHash, + bytes memory moduleSignature, + address smartAccount + ) external view returns (bytes4); +} diff --git a/contracts/smart-account/interfaces/modules/IPasskeyRegistryModule.sol b/contracts/smart-account/interfaces/modules/IPasskeyRegistryModule.sol new file mode 100644 index 000000000..77dda067e --- /dev/null +++ b/contracts/smart-account/interfaces/modules/IPasskeyRegistryModule.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +/** + * @title Passkey ownership Authorization module for Biconomy Smart Accounts. + * @dev Compatible with Biconomy Modular Interface v 0.2 + * - It allows to validate user operations signed by passkeys. + * - One owner per Smart Account. + * For Smart Contract Owners check SmartContractOwnership module instead + * @author Aman Raj - + */ +interface IPasskeyRegistryModule { + error NoPassKeyRegisteredForSmartAccount(address smartAccount); + error AlreadyInitedForSmartAccount(address smartAccount); + + /** + * @dev Initializes the module for a Smart Account. + * Should be used at a time of first enabling the module for a Smart Account. + * @param _pubKeyX The x coordinate of the public key. + * @param _pubKeyY The y coordinate of the public key. + * @param _keyId The keyId of the Smart Account. + * @return address of the module. + */ + function initForSmartAccount( + uint256 _pubKeyX, + uint256 _pubKeyY, + string calldata _keyId + ) external returns (address); + + function isValidSignatureForAddress( + bytes32 signedDataHash, + bytes memory moduleSignature + ) external view returns (bytes4); +} diff --git a/contracts/smart-account/interfaces/modules/ISessionKeyManagerModule.sol b/contracts/smart-account/interfaces/modules/ISessionKeyManagerModule.sol new file mode 100644 index 000000000..e32adbdd6 --- /dev/null +++ b/contracts/smart-account/interfaces/modules/ISessionKeyManagerModule.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +/** + * @title Session Key Manager module for Biconomy Modular Smart Accounts. + * @dev Performs basic verifications for every session key signed userOp. + * Checks if the session key has been enabled, that it is not due and has not yet expired + * Then passes the validation flow to appropriate Session Validation module + * - For the sake of efficiency and flexibility, doesn't limit what operations + * Session Validation modules can perform + * - Should be used with carefully verified and audited Session Validation Modules only + * - Compatible with Biconomy Modular Interface v 0.1 + * @author Fil Makarov - + */ +interface ISessionKeyManagerModule { + struct SessionStorage { + bytes32 merkleRoot; + } + + /** + * @dev validates that Session Key + parameters are enabled + * by being included into the merkle tree + * @param userOpSender smartAccount for which session key is being validated + * @param validUntil timestamp when the session key expires + * @param validAfter timestamp when the session key becomes valid + * @param sessionValidationModule address of the Session Validation Module + * @param sessionKeyData session parameters (limitations/permissions) + * @param merkleProof merkle proof for the leaf which represents this session key + params + * @dev if doesn't revert, session key is considered valid + */ + function validateSessionKey( + address userOpSender, + uint48 validUntil, + uint48 validAfter, + address sessionValidationModule, + bytes calldata sessionKeyData, + bytes32[] calldata merkleProof + ) external; + + /** + * @dev sets the merkle root of a tree containing session keys for msg.sender + * should be called by Smart Account + * @param _merkleRoot Merkle Root of a tree that contains session keys with their permissions and parameters + */ + function setMerkleRoot(bytes32 _merkleRoot) external; + + /** + * @dev returns the SessionStorage object for a given smartAccount + * @param smartAccount Smart Account address + */ + function getSessionKeys( + address smartAccount + ) external view returns (SessionStorage memory); +} diff --git a/contracts/smart-account/modules/SessionValidationModules/ISessionValidationModule.sol b/contracts/smart-account/interfaces/modules/ISessionValidationModule.sol similarity index 97% rename from contracts/smart-account/modules/SessionValidationModules/ISessionValidationModule.sol rename to contracts/smart-account/interfaces/modules/ISessionValidationModule.sol index d3aa6d369..e6437c283 100644 --- a/contracts/smart-account/modules/SessionValidationModules/ISessionValidationModule.sol +++ b/contracts/smart-account/interfaces/modules/ISessionValidationModule.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import "../BaseAuthorizationModule.sol"; import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; abstract contract ISessionValidationModule { diff --git a/contracts/smart-account/interfaces/modules/ISmartContractOwnershipRegistryModule.sol b/contracts/smart-account/interfaces/modules/ISmartContractOwnershipRegistryModule.sol new file mode 100644 index 000000000..83767ee7d --- /dev/null +++ b/contracts/smart-account/interfaces/modules/ISmartContractOwnershipRegistryModule.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +/** + * @title Smart Contract Ownership Authorization module for Biconomy Smart Accounts. + * @dev Compatible with Biconomy Modular Interface v 0.1 + * - It allows to validate user operations signed by other smart contracts via EIP-1271. + * - EIP-1271 compatible (ensures Smart Account can validate signed messages). + * - One owner per Smart Account. + * @dev No EOA owners supported + * For EOA Owners check EcdsaOwnership module instead + * @notice !!! This module doesn't follow the Storage Access Rules set by ERC-4337 !!! + * https://eips.ethereum.org/EIPS/eip-4337#storage-associated-with-an-address + * Thus it will not be compatible with the standard bundlers. + * You can still use it in private environments or with custom bundlers which have + * less restrictions than ones participating in the unified userOps mempool. + * + * @author Fil Makarov - + */ +interface ISmartContractOwnershipRegistryModule { + event OwnershipTransferred( + address indexed smartAccount, + address indexed oldOwner, + address indexed newOwner + ); + + error NoOwnerRegisteredForSmartAccount(address smartAccount); + error AlreadyInitedForSmartAccount(address smartAccount); + error WrongSignatureLength(); + error NotSmartContract(address account); + + /** + * @dev Initializes the module for a Smart Account. + * @dev no need to check for address(0) as it is not a Smart Contract + * Should be used at a time of first enabling the module for a Smart Account. + * @param owner The owner of the Smart Account. + */ + function initForSmartAccount(address owner) external returns (address); + + /** + * @dev Sets/changes an for a Smart Account. + * @dev no need to check for address(0) as it is not a Smart Contract + * Should be called by Smart Account itself. + * @param owner The owner of the Smart Account. + */ + function transferOwnership(address owner) external; + + /** + * @dev Renounces ownership + * should be called by Smart Account. + */ + function renounceOwnership() external; + + /** + * @dev Returns the owner of the Smart Account. Reverts for Smart Accounts without owners. + * @param smartAccount Smart Account address. + * @return owner The owner of the Smart Account. + */ + function getOwner(address smartAccount) external view returns (address); + + /** + * @dev Validates a signature for a message signed by address. + * @param dataHash Exact hash of the data that was signed. + * @param moduleSignature Signature to be validated. + * @param smartAccount expected signer Smart Account address. + * @return EIP1271_MAGIC_VALUE if signature is valid, 0xffffffff otherwise. + */ + function isValidSignatureForAddress( + bytes32 dataHash, + bytes memory moduleSignature, + address smartAccount + ) external view returns (bytes4); +} diff --git a/contracts/smart-account/libs/Math.sol b/contracts/smart-account/libs/Math.sol deleted file mode 100644 index 688a96af8..000000000 --- a/contracts/smart-account/libs/Math.sol +++ /dev/null @@ -1,368 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) - -pragma solidity 0.8.17; - -/** - * @dev Standard math utilities missing in the Solidity language. - */ -library Math { - enum Rounding { - Down, // Toward negative infinity - Up, // Toward infinity - Zero // Toward zero - } - - /** - * @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 up instead - * of rounding down. - */ - function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { - // (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; // Least significant 256 bits of the product - uint256 prod1; // Most significant 256 bits of the product - assembly { - let mm := mulmod(x, y, not(0)) - prod0 := mul(x, y) - 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. - require(denominator > prod1, "Math: mulDiv overflow"); - - /////////////////////////////////////////////// - // 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. - - // Does not overflow because the denominator cannot be zero at this stage in the function. - uint256 twos = denominator & (~denominator + 1); - 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 (rounding == Rounding.Up && 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 down. - * - * 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 + - (rounding == Rounding.Up && result * result < a ? 1 : 0); - } - } - - /** - * @dev Return the log in base 2, rounded down, of a positive value. - * 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 + - (rounding == Rounding.Up && 1 << result < value ? 1 : 0); - } - } - - /** - * @dev Return the log in base 10, rounded down, of a positive value. - * 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 + - (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); - } - } - - /** - * @dev Return the log in base 256, rounded down, of a positive value. - * 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 + - (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); - } - } -} diff --git a/contracts/smart-account/libs/MultiSend.sol b/contracts/smart-account/libs/MultiSend.sol deleted file mode 100644 index c7e1e5afe..000000000 --- a/contracts/smart-account/libs/MultiSend.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.17; - -/// @title Multi Send - Allows to batch multiple transactions into one. -/// @author Nick Dodson - -/// @author Gonçalo Sá - -/// @author Stefan George - -/// @author Richard Meissner - -contract MultiSend { - address private immutable _multisendSingleton; - - constructor() { - _multisendSingleton = address(this); - } - - /// @dev Sends multiple transactions and reverts all if one fails. - /// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of - /// operation as a uint8 with 0 for a call or 1 for a delegatecall (=> 1 byte), - /// to as a address (=> 20 bytes), - /// value as a uint256 (=> 32 bytes), - /// data length as a uint256 (=> 32 bytes), - /// data as bytes. - /// see abi.encodePacked for more information on packed encoding - /// @notice This method is payable as delegatecalls keep the msg.value from the previous call - /// If the calling method (e.g. execTransaction) received ETH this would revert otherwise - function multiSend(bytes memory transactions) external { - require( - address(this) != _multisendSingleton, - "should be called via delegatecall" - ); - - assembly { - let length := mload(transactions) - let i := 0x20 - for { - // Pre block is not used in "while mode" - } lt(i, length) { - // Post block is not used in "while mode" - } { - // First byte of the data is the operation. - // We shift by 248 bits (256 - 8 [operation byte]) it right since mload will always load 32 bytes (a word). - // This will also zero out unused data. - let operation := shr(0xf8, mload(add(transactions, i))) - // We offset the load address by 1 byte (operation byte) - // We shift it right by 96 bits (256 - 160 [20 address bytes]) to right-align the data and zero out unused data. - let to := shr(0x60, mload(add(transactions, add(i, 0x01)))) - // We offset the load address by 21 byte (operation byte + 20 address bytes) - let value := mload(add(transactions, add(i, 0x15))) - // We offset the load address by 53 byte (operation byte + 20 address bytes + 32 value bytes) - let dataLength := mload(add(transactions, add(i, 0x35))) - // We offset the load address by 85 byte (operation byte + 20 address bytes + 32 value bytes + 32 data length bytes) - let data := add(transactions, add(i, 0x55)) - let success := 0 - switch operation - case 0 { - success := call(gas(), to, value, data, dataLength, 0, 0) - } - case 1 { - success := delegatecall(gas(), to, data, dataLength, 0, 0) - } - if eq(success, 0) { - revert(0, 0) - } - // Next entry starts at 85 byte + data length - i := add(i, add(0x55, dataLength)) - } - } - } -} diff --git a/contracts/smart-account/libs/MultiSendCallOnly.sol b/contracts/smart-account/libs/MultiSendCallOnly.sol deleted file mode 100644 index 045bce0a8..000000000 --- a/contracts/smart-account/libs/MultiSendCallOnly.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.17; - -/// @title Multi Send Call Only - Allows to batch multiple transactions into one, but only calls -/// @author Stefan George - -/// @author Richard Meissner - -/// @notice The guard logic is not required here as this contract doesn't support nested delegate calls -contract MultiSendCallOnly { - /// @dev Sends multiple transactions and reverts all if one fails. - /// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of - /// operation has to be uint8(0) in this version (=> 1 byte), - /// to as a address (=> 20 bytes), - /// value as a uint256 (=> 32 bytes), - /// data length as a uint256 (=> 32 bytes), - /// data as bytes. - /// see abi.encodePacked for more information on packed encoding - /// @notice The code is for most part the same as the normal MultiSend (to keep compatibility), - /// but reverts if a transaction tries to use a delegatecall. - /// @notice This method is payable as delegatecalls keep the msg.value from the previous call - /// If the calling method (e.g. execTransaction) received ETH this would revert otherwise - function multiSend(bytes memory transactions) external { - assembly { - let length := mload(transactions) - let i := 0x20 - for { - // Pre block is not used in "while mode" - } lt(i, length) { - // Post block is not used in "while mode" - } { - // First byte of the data is the operation. - // We shift by 248 bits (256 - 8 [operation byte]) it right since mload will always load 32 bytes (a word). - // This will also zero out unused data. - let operation := shr(0xf8, mload(add(transactions, i))) - // We offset the load address by 1 byte (operation byte) - // We shift it right by 96 bits (256 - 160 [20 address bytes]) to right-align the data and zero out unused data. - let to := shr(0x60, mload(add(transactions, add(i, 0x01)))) - // We offset the load address by 21 byte (operation byte + 20 address bytes) - let value := mload(add(transactions, add(i, 0x15))) - // We offset the load address by 53 byte (operation byte + 20 address bytes + 32 value bytes) - let dataLength := mload(add(transactions, add(i, 0x35))) - // We offset the load address by 85 byte (operation byte + 20 address bytes + 32 value bytes + 32 data length bytes) - let data := add(transactions, add(i, 0x55)) - let success := 0 - switch operation - case 0 { - success := call(gas(), to, value, data, dataLength, 0, 0) - } - // This version does not allow delegatecalls - case 1 { - revert(0, 0) - } - if eq(success, 0) { - revert(0, 0) - } - // Next entry starts at 85 byte + data length - i := add(i, add(0x55, dataLength)) - } - } - } -} diff --git a/contracts/smart-account/libs/Strings.sol b/contracts/smart-account/libs/Strings.sol deleted file mode 100644 index 1d2295df7..000000000 --- a/contracts/smart-account/libs/Strings.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) - -pragma solidity 0.8.17; - -import "./Math.sol"; - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @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), _SYMBOLS)) - } - value /= 10; - if (value == 0) break; - } - return buffer; - } - } - - /** - * @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) { - 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] = _SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - 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); - } -} diff --git a/contracts/smart-account/modules/BaseAuthorizationModule.sol b/contracts/smart-account/modules/BaseAuthorizationModule.sol index d9aee05c1..578f33d05 100644 --- a/contracts/smart-account/modules/BaseAuthorizationModule.sol +++ b/contracts/smart-account/modules/BaseAuthorizationModule.sol @@ -3,8 +3,7 @@ pragma solidity 0.8.17; /* solhint-disable no-empty-blocks */ -import {IAuthorizationModule} from "../interfaces/IAuthorizationModule.sol"; -import {ISignatureValidator} from "../interfaces/ISignatureValidator.sol"; +import {IBaseAuthorizationModule} from "../interfaces/modules/IBaseAuthorizationModule.sol"; contract AuthorizationModulesConstants { uint256 internal constant VALIDATION_SUCCESS = 0; @@ -12,7 +11,6 @@ contract AuthorizationModulesConstants { } abstract contract BaseAuthorizationModule is - IAuthorizationModule, - ISignatureValidator, + IBaseAuthorizationModule, AuthorizationModulesConstants {} diff --git a/contracts/smart-account/modules/BatchedSessionRouterModule.sol b/contracts/smart-account/modules/BatchedSessionRouterModule.sol index cfbe5d511..f4c7da298 100644 --- a/contracts/smart-account/modules/BatchedSessionRouterModule.sol +++ b/contracts/smart-account/modules/BatchedSessionRouterModule.sol @@ -1,51 +1,42 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -/* solhint-disable function-max-lines */ +/* solhint-disable function-max-lines,no-unused-import */ import {BaseAuthorizationModule} from "./BaseAuthorizationModule.sol"; -import {ISessionValidationModule} from "./SessionValidationModules/ISessionValidationModule.sol"; -import {ISessionKeyManager} from "../interfaces/ISessionKeyManager.sol"; +import {ISessionValidationModule} from "../interfaces/modules/ISessionValidationModule.sol"; +import {ISessionKeyManagerModule} from "../interfaces/modules/ISessionKeyManagerModule.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "@account-abstraction/contracts/core/Helpers.sol"; +import {_packValidationData} from "@account-abstraction/contracts/core/Helpers.sol"; import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; +import {IBatchedSessionRouterModule} from "../interfaces/modules/IBatchedSessionRouterModule.sol"; +import {IAuthorizationModule} from "../interfaces/IAuthorizationModule.sol"; +import {ISignatureValidator} from "../interfaces/ISignatureValidator.sol"; /** * @title Batched Session Router * @dev Built to process executeBatch and executeBatch_y6U calls * - Every call inside batch should be covered by an appropriate Session Validation Module - * - Parses data provided and sequentially - a) verifies the session key was enabled via SessionKeyManager - b) verifies the session key permissions via Session Validation Modules + * - Parses data provided and sequentially + * a) verifies the session key was enabled via SessionKeyManager + * b) verifies the session key permissions via Session Validation Modules * - Should be used with carefully verified and audited Session Validation Modules only * - Compatible with Biconomy Modular Interface v 0.1 * @author Fil Makarov - */ -contract BatchedSessionRouter is BaseAuthorizationModule { - struct SessionData { - uint48 validUntil; - uint48 validAfter; - address sessionValidationModule; - bytes sessionKeyData; - bytes32[] merkleProof; - bytes callSpecificData; - } - +contract BatchedSessionRouter is + BaseAuthorizationModule, + IBatchedSessionRouterModule +{ bytes4 public constant EXECUTE_BATCH_SELECTOR = 0x47e1da2a; bytes4 public constant EXECUTE_BATCH_OPTIMIZED_SELECTOR = 0x00004680; - /** - * @dev validates userOperation. Expects userOp.callData to be an executeBatch - * or executeBatch_y6U call. If something goes wrong, reverts. - * @param userOp User Operation to be validated. - * @param userOpHash Hash of the User Operation to be validated. - * @return SIG_VALIDATION_FAILED or packed validation result. - */ + /// @inheritdoc IAuthorizationModule function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash - ) external virtual returns (uint256) { + ) external virtual override returns (uint256) { // check this is a proper method call bytes4 selector = bytes4(userOp.callData[0:4]); require( @@ -89,7 +80,7 @@ contract BatchedSessionRouter is BaseAuthorizationModule { for (uint256 i; i < length; ) { // validate the sessionKey // sessionKeyManager reverts if something wrong - ISessionKeyManager(sessionKeyManager).validateSessionKey( + ISessionKeyManagerModule(sessionKeyManager).validateSessionKey( userOp.sender, sessionData[i].validUntil, sessionData[i].validAfter, @@ -139,6 +130,7 @@ contract BatchedSessionRouter is BaseAuthorizationModule { } /** + * @inheritdoc ISignatureValidator * @dev isValidSignature according to BaseAuthorizationModule * @param _dataHash Hash of the data to be validated. * @param _signature Signature over the the _dataHash. @@ -147,7 +139,7 @@ contract BatchedSessionRouter is BaseAuthorizationModule { function isValidSignature( bytes32 _dataHash, bytes memory _signature - ) public view override returns (bytes4) { + ) public pure override returns (bytes4) { (_dataHash, _signature); return 0xffffffff; // do not support it here } diff --git a/contracts/smart-account/modules/EcdsaOwnershipRegistryModule.sol b/contracts/smart-account/modules/EcdsaOwnershipRegistryModule.sol index a7ca94d86..b30a15de1 100644 --- a/contracts/smart-account/modules/EcdsaOwnershipRegistryModule.sol +++ b/contracts/smart-account/modules/EcdsaOwnershipRegistryModule.sol @@ -1,9 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; +/* solhint-disable no-unused-import */ + import {BaseAuthorizationModule} from "./BaseAuthorizationModule.sol"; +import {EIP1271_MAGIC_VALUE} from "contracts/smart-account/interfaces/ISignatureValidator.sol"; import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {IEcdsaOwnershipRegistryModule} from "../interfaces/modules/IEcdsaOwnershipRegistryModule.sol"; +import {IAuthorizationModule} from "../interfaces/IAuthorizationModule.sol"; +import {ISignatureValidator} from "../interfaces/ISignatureValidator.sol"; /** * @title ECDSA ownership Authorization module for Biconomy Smart Accounts. @@ -18,79 +24,56 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; * @author Fil Makarov - */ -contract EcdsaOwnershipRegistryModule is BaseAuthorizationModule { +contract EcdsaOwnershipRegistryModule is + BaseAuthorizationModule, + IEcdsaOwnershipRegistryModule +{ using ECDSA for bytes32; string public constant NAME = "ECDSA Ownership Registry Module"; string public constant VERSION = "0.2.0"; mapping(address => address) internal _smartAccountOwners; - event OwnershipTransferred( - address indexed smartAccount, - address indexed oldOwner, - address indexed newOwner - ); - - error NoOwnerRegisteredForSmartAccount(address smartAccount); - error AlreadyInitedForSmartAccount(address smartAccount); - error WrongSignatureLength(); - error NotEOA(address account); - error ZeroAddressNotAllowedAsOwner(); - - /** - * @dev Initializes the module for a Smart Account. - * Should be used at a time of first enabling the module for a Smart Account. - * @param eoaOwner The owner of the Smart Account. Should be EOA! - */ - function initForSmartAccount(address eoaOwner) external returns (address) { - if (_smartAccountOwners[msg.sender] != address(0)) + /// @inheritdoc IEcdsaOwnershipRegistryModule + function initForSmartAccount( + address eoaOwner + ) external override returns (address) { + if (_smartAccountOwners[msg.sender] != address(0)) { revert AlreadyInitedForSmartAccount(msg.sender); + } if (eoaOwner == address(0)) revert ZeroAddressNotAllowedAsOwner(); _smartAccountOwners[msg.sender] = eoaOwner; return address(this); } - /** - * @dev Sets/changes an for a Smart Account. - * Should be called by Smart Account itself. - * @param owner The owner of the Smart Account. - */ - function transferOwnership(address owner) external { + /// @inheritdoc IEcdsaOwnershipRegistryModule + function transferOwnership(address owner) external override { if (_isSmartContract(owner)) revert NotEOA(owner); if (owner == address(0)) revert ZeroAddressNotAllowedAsOwner(); _transferOwnership(msg.sender, owner); } - /** - * @dev Renounces ownership - * should be called by Smart Account. - */ - function renounceOwnership() external { + /// @inheritdoc IEcdsaOwnershipRegistryModule + function renounceOwnership() external override { _transferOwnership(msg.sender, address(0)); } - /** - * @dev Returns the owner of the Smart Account. Reverts for Smart Accounts without owners. - * @param smartAccount Smart Account address. - * @return owner The owner of the Smart Account. - */ - function getOwner(address smartAccount) external view returns (address) { + /// @inheritdoc IEcdsaOwnershipRegistryModule + function getOwner( + address smartAccount + ) external view override returns (address) { address owner = _smartAccountOwners[smartAccount]; - if (owner == address(0)) + if (owner == address(0)) { revert NoOwnerRegisteredForSmartAccount(smartAccount); + } return owner; } - /** - * @dev validates userOperation - * @param userOp User Operation to be validated. - * @param userOpHash Hash of the User Operation to be validated. - * @return sigValidationResult 0 if signature is valid, SIG_VALIDATION_FAILED otherwise. - */ + /// @inheritdoc IAuthorizationModule function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash - ) external view virtual returns (uint256) { + ) external view virtual override returns (uint256) { (bytes memory cleanEcdsaSignature, ) = abi.decode( userOp.signature, (bytes, address) @@ -102,6 +85,7 @@ contract EcdsaOwnershipRegistryModule is BaseAuthorizationModule { } /** + * @inheritdoc ISignatureValidator * @dev Validates a signature for a message. * To be called from a Smart Account. * @param dataHash Exact hash of the data that was signed. @@ -116,19 +100,12 @@ contract EcdsaOwnershipRegistryModule is BaseAuthorizationModule { isValidSignatureForAddress(dataHash, moduleSignature, msg.sender); } - /** - * @dev Validates a signature for a message signed by address. - * @dev Also try dataHash.toEthSignedMessageHash() - * @param dataHash hash of the data - * @param moduleSignature Signature to be validated. - * @param smartAccount expected signer Smart Account address. - * @return EIP1271_MAGIC_VALUE if signature is valid, 0xffffffff otherwise. - */ + /// @inheritdoc IEcdsaOwnershipRegistryModule function isValidSignatureForAddress( bytes32 dataHash, bytes memory moduleSignature, address smartAccount - ) public view virtual returns (bytes4) { + ) public view virtual override returns (bytes4) { if (_verifySignature(dataHash, moduleSignature, smartAccount)) { return EIP1271_MAGIC_VALUE; } @@ -165,8 +142,9 @@ contract EcdsaOwnershipRegistryModule is BaseAuthorizationModule { address smartAccount ) internal view returns (bool) { address expectedSigner = _smartAccountOwners[smartAccount]; - if (expectedSigner == address(0)) + if (expectedSigner == address(0)) { revert NoOwnerRegisteredForSmartAccount(smartAccount); + } if (signature.length < 65) revert WrongSignatureLength(); address recovered = (dataHash.toEthSignedMessageHash()).recover( signature diff --git a/contracts/smart-account/modules/Exotic/EcdsaEthSignSupportOwnershipRegistryModule.sol b/contracts/smart-account/modules/Exotic/EcdsaEthSignSupportOwnershipRegistryModule.sol index 07611b70f..378f57b31 100644 --- a/contracts/smart-account/modules/Exotic/EcdsaEthSignSupportOwnershipRegistryModule.sol +++ b/contracts/smart-account/modules/Exotic/EcdsaEthSignSupportOwnershipRegistryModule.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.17; import {BaseAuthorizationModule} from "../BaseAuthorizationModule.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; +import {EIP1271_MAGIC_VALUE} from "contracts/smart-account/interfaces/ISignatureValidator.sol"; /** * @title ECDSA ownership Authorization module for Biconomy Smart Accounts. diff --git a/contracts/smart-account/modules/ForwardFlowModule.sol b/contracts/smart-account/modules/ForwardFlowModule.sol index 1818be5cb..b91f25fb9 100644 --- a/contracts/smart-account/modules/ForwardFlowModule.sol +++ b/contracts/smart-account/modules/ForwardFlowModule.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {ISignatureValidator, ISignatureValidatorConstants} from "../interfaces/ISignatureValidator.sol"; +import {ISignatureValidator, EIP1271_MAGIC_VALUE} from "../interfaces/ISignatureValidator.sol"; import {Enum} from "../common/Enum.sol"; import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; -import {Math} from "../libs/Math.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; + +/* solhint-disable function-max-lines */ struct Transaction { address to; @@ -80,7 +82,7 @@ interface IExecFromModule { ) external returns (bool success); } -contract ForwardFlowModule is ReentrancyGuard, ISignatureValidatorConstants { +contract ForwardFlowModule is ReentrancyGuard { // Domain Seperators keccak256("EIP712Domain(uint256 chainId,address verifyingContract)"); bytes32 internal constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218; @@ -194,7 +196,9 @@ contract ForwardFlowModule is ReentrancyGuard, ISignatureValidatorConstants { data, operation ) - ) revert ExecutionFailed(); + ) { + revert ExecutionFailed(); + } // Convert response to string and return via error message unchecked { revert(string(abi.encodePacked(startGas - gasleft()))); @@ -212,7 +216,6 @@ contract ForwardFlowModule is ReentrancyGuard, ISignatureValidatorConstants { * Should be a signature over Typed Data Hash * Use eth_signTypedData, not a personal_sign */ - function execTransaction( address smartAccount, Transaction memory _tx, @@ -253,12 +256,13 @@ contract ForwardFlowModule is ReentrancyGuard, ISignatureValidatorConstants { if ( gasleft() < Math.max((_tx.targetTxGas << 6) / 63, _tx.targetTxGas + 2500) + 7500 - ) + ) { revert NotEnoughGasLeft( gasleft(), Math.max((_tx.targetTxGas << 6) / 63, _tx.targetTxGas + 2500) + 7500 ); + } // Use scope here to limit variable lifetime and prevent `stack too deep` errors { //we always provide targetTxGas to execution @@ -272,12 +276,13 @@ contract ForwardFlowModule is ReentrancyGuard, ISignatureValidatorConstants { // If targetTxGas and gasPrice are both 0, the internal tx must succeed. // Enables safe use of `estimateGas` by finding the minimum gas where the transaction doesn't revert - if (!success && _tx.targetTxGas == 0 && refundInfo.gasPrice == 0) + if (!success && _tx.targetTxGas == 0 && refundInfo.gasPrice == 0) { revert CanNotEstimateGas( _tx.targetTxGas, refundInfo.gasPrice, success ); + } // Transfer transaction costs to tx.origin to avoid intermediate contract payments. uint256 payment; diff --git a/contracts/smart-account/modules/MultichainECDSAValidator.sol b/contracts/smart-account/modules/MultichainECDSAValidator.sol index d2f02d33c..9a8fdc56e 100644 --- a/contracts/smart-account/modules/MultichainECDSAValidator.sol +++ b/contracts/smart-account/modules/MultichainECDSAValidator.sol @@ -24,6 +24,7 @@ contract MultichainECDSAValidator is EcdsaOwnershipRegistryModule { using UserOperationLib for UserOperation; /** + * @inheritdoc EcdsaOwnershipRegistryModule * @dev Validates User Operation. * leaf = validUntil + validAfter + userOpHash * If the leaf is the part of the Tree with a root provided, userOp considered diff --git a/contracts/smart-account/modules/PasskeyRegistryModule.sol b/contracts/smart-account/modules/PasskeyRegistryModule.sol index 1894953b7..ab6efe18d 100644 --- a/contracts/smart-account/modules/PasskeyRegistryModule.sol +++ b/contracts/smart-account/modules/PasskeyRegistryModule.sol @@ -5,6 +5,10 @@ import {BaseAuthorizationModule} from "./BaseAuthorizationModule.sol"; import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; import {Base64} from "@openzeppelin/contracts/utils/Base64.sol"; import {Secp256r1, PassKeyId} from "./PasskeyValidationModules/Secp256r1.sol"; +import {EIP1271_MAGIC_VALUE} from "contracts/smart-account/interfaces/ISignatureValidator.sol"; +import {IPasskeyRegistryModule} from "../interfaces/modules/IPasskeyRegistryModule.sol"; +import {ISignatureValidator} from "../interfaces/ISignatureValidator.sol"; +import {IAuthorizationModule} from "../interfaces/IAuthorizationModule.sol"; /** * @title Passkey ownership Authorization module for Biconomy Smart Accounts. @@ -14,33 +18,27 @@ import {Secp256r1, PassKeyId} from "./PasskeyValidationModules/Secp256r1.sol"; * For Smart Contract Owners check SmartContractOwnership module instead * @author Aman Raj - */ - -contract PasskeyRegistryModule is BaseAuthorizationModule { +contract PasskeyRegistryModule is + BaseAuthorizationModule, + IPasskeyRegistryModule +{ string public constant NAME = "PassKeys Ownership Registry Module"; string public constant VERSION = "0.2.0"; mapping(address => PassKeyId) public smartAccountPassKeys; - error NoPassKeyRegisteredForSmartAccount(address smartAccount); - error AlreadyInitedForSmartAccount(address smartAccount); - - /** - * @dev Initializes the module for a Smart Account. - * Should be used at a time of first enabling the module for a Smart Account. - * @param _pubKeyX The x coordinate of the public key. - * @param _pubKeyY The y coordinate of the public key. - * @param _keyId The keyId of the Smart Account. - * @return address of the module. - */ + /// @inheritdoc IPasskeyRegistryModule function initForSmartAccount( uint256 _pubKeyX, uint256 _pubKeyY, string calldata _keyId - ) external returns (address) { + ) external override returns (address) { if ( smartAccountPassKeys[msg.sender].pubKeyX != 0 && smartAccountPassKeys[msg.sender].pubKeyY != 0 - ) revert AlreadyInitedForSmartAccount(msg.sender); + ) { + revert AlreadyInitedForSmartAccount(msg.sender); + } smartAccountPassKeys[msg.sender] = PassKeyId( _pubKeyX, _pubKeyY, @@ -49,12 +47,7 @@ contract PasskeyRegistryModule is BaseAuthorizationModule { return address(this); } - /** - * @dev validates userOperation - * @param userOp User Operation to be validated. - * @param userOpHash Hash of the User Operation to be validated. - * @return sigValidationResult 0 if signature is valid, SIG_VALIDATION_FAILED otherwise. - */ + /// @inheritdoc IAuthorizationModule function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash @@ -62,6 +55,7 @@ contract PasskeyRegistryModule is BaseAuthorizationModule { return _validateSignature(userOp, userOpHash); } + /// @inheritdoc ISignatureValidator function isValidSignature( bytes32 signedDataHash, bytes memory moduleSignature @@ -69,10 +63,11 @@ contract PasskeyRegistryModule is BaseAuthorizationModule { return isValidSignatureForAddress(signedDataHash, moduleSignature); } + /// @inheritdoc IPasskeyRegistryModule function isValidSignatureForAddress( bytes32 signedDataHash, bytes memory moduleSignature - ) public view virtual returns (bytes4) { + ) public view virtual override returns (bytes4) { if (_verifySignature(signedDataHash, moduleSignature)) { return EIP1271_MAGIC_VALUE; } @@ -107,8 +102,9 @@ contract PasskeyRegistryModule is BaseAuthorizationModule { bytes32 sigHash = sha256(bytes.concat(authenticatorData, clientHash)); PassKeyId memory passKey = smartAccountPassKeys[msg.sender]; - if (passKey.pubKeyX == 0 && passKey.pubKeyY == 0) + if (passKey.pubKeyX == 0 && passKey.pubKeyY == 0) { revert NoPassKeyRegisteredForSmartAccount(msg.sender); + } return Secp256r1.verify(passKey, sigx, sigy, uint256(sigHash)); } diff --git a/contracts/smart-account/modules/SessionKeyManagerModule.sol b/contracts/smart-account/modules/SessionKeyManagerModule.sol index f22e5a9d1..b3d2fac0b 100644 --- a/contracts/smart-account/modules/SessionKeyManagerModule.sol +++ b/contracts/smart-account/modules/SessionKeyManagerModule.sol @@ -2,14 +2,13 @@ pragma solidity 0.8.17; import {BaseAuthorizationModule} from "./BaseAuthorizationModule.sol"; -import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; -import "@account-abstraction/contracts/core/Helpers.sol"; -import "./SessionValidationModules/ISessionValidationModule.sol"; -import {ISessionKeyManager} from "../interfaces/ISessionKeyManager.sol"; - -struct SessionStorage { - bytes32 merkleRoot; -} +import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; +import {_packValidationData} from "@account-abstraction/contracts/core/Helpers.sol"; +import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; +import {ISessionValidationModule} from "../interfaces/modules/ISessionValidationModule.sol"; +import {ISessionKeyManagerModule} from "../interfaces/modules/ISessionKeyManagerModule.sol"; +import {IAuthorizationModule} from "../interfaces/IAuthorizationModule.sol"; +import {ISignatureValidator} from "../interfaces/ISignatureValidator.sol"; /** * @title Session Key Manager module for Biconomy Modular Smart Accounts. @@ -23,29 +22,23 @@ struct SessionStorage { * @author Fil Makarov - */ -contract SessionKeyManager is BaseAuthorizationModule, ISessionKeyManager { +contract SessionKeyManager is + BaseAuthorizationModule, + ISessionKeyManagerModule +{ /** * @dev mapping of Smart Account to a SessionStorage * Info about session keys is stored as root of the merkle tree built over the session keys */ mapping(address => SessionStorage) internal _userSessions; - /** - * @dev sets the merkle root of a tree containing session keys for msg.sender - * should be called by Smart Account - * @param _merkleRoot Merkle Root of a tree that contains session keys with their permissions and parameters - */ - function setMerkleRoot(bytes32 _merkleRoot) external { + /// @inheritdoc ISessionKeyManagerModule + function setMerkleRoot(bytes32 _merkleRoot) external override { _userSessions[msg.sender].merkleRoot = _merkleRoot; // TODO:should we add an event here? which emits the new merkle root } - /** - * @dev validates userOperation - * @param userOp User Operation to be validated. - * @param userOpHash Hash of the User Operation to be validated. - * @return sigValidationResult 0 if signature is valid, SIG_VALIDATION_FAILED otherwise. - */ + /// @inheritdoc IAuthorizationModule function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash @@ -90,28 +83,14 @@ contract SessionKeyManager is BaseAuthorizationModule, ISessionKeyManager { ); } - /** - * @dev returns the SessionStorage object for a given smartAccount - * @param smartAccount Smart Account address - */ + /// @inheritdoc ISessionKeyManagerModule function getSessionKeys( address smartAccount - ) external view returns (SessionStorage memory) { + ) external view override returns (SessionStorage memory) { return _userSessions[smartAccount]; } - /** - * @dev validates that Session Key + parameters are enabled - * by being included into the merkle tree - * @param smartAccount smartAccount for which session key is being validated - * @param validUntil timestamp when the session key expires - * @param validAfter timestamp when the session key becomes valid - * @param sessionValidationModule address of the Session Validation Module - * @param sessionKeyData session parameters (limitations/permissions) - * @param merkleProof merkle proof for the leaf which represents this session key + params - * @dev if doesn't revert, session key is considered valid - */ - + /// @inheritdoc ISessionKeyManagerModule function validateSessionKey( address smartAccount, uint48 validUntil, @@ -138,16 +117,11 @@ contract SessionKeyManager is BaseAuthorizationModule, ISessionKeyManager { } } - /** - * @dev isValidSignature according to BaseAuthorizationModule - * @param _dataHash Hash of the data to be validated. - * @param _signature Signature over the the _dataHash. - * @return always returns 0xffffffff as signing messages is not supported by SessionKeys - */ + /// @inheritdoc ISignatureValidator function isValidSignature( bytes32 _dataHash, bytes memory _signature - ) public view override returns (bytes4) { + ) public pure override returns (bytes4) { (_dataHash, _signature); return 0xffffffff; // do not support it here } diff --git a/contracts/smart-account/modules/SessionValidationModules/ERC20SessionValidationModule.sol b/contracts/smart-account/modules/SessionValidationModules/ERC20SessionValidationModule.sol index c5d60435e..333fe39d1 100644 --- a/contracts/smart-account/modules/SessionValidationModules/ERC20SessionValidationModule.sol +++ b/contracts/smart-account/modules/SessionValidationModules/ERC20SessionValidationModule.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import "./ISessionValidationModule.sol"; +import "../../interfaces/modules/ISessionValidationModule.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; /** diff --git a/contracts/smart-account/modules/SmartContractOwnershipRegistryModule.sol b/contracts/smart-account/modules/SmartContractOwnershipRegistryModule.sol index 6362ee429..a69e72dac 100644 --- a/contracts/smart-account/modules/SmartContractOwnershipRegistryModule.sol +++ b/contracts/smart-account/modules/SmartContractOwnershipRegistryModule.sol @@ -1,9 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {BaseAuthorizationModule, ISignatureValidator} from "./BaseAuthorizationModule.sol"; +import {BaseAuthorizationModule} from "./BaseAuthorizationModule.sol"; +import {ISignatureValidator} from "../interfaces/ISignatureValidator.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; +import {EIP1271_MAGIC_VALUE} from "contracts/smart-account/interfaces/ISignatureValidator.sol"; +import {ISmartContractOwnershipRegistryModule} from "../interfaces/modules/ISmartContractOwnershipRegistryModule.sol"; +import {IAuthorizationModule} from "../interfaces/IAuthorizationModule.sol"; /** * @title Smart Contract Ownership Authorization module for Biconomy Smart Accounts. @@ -22,75 +26,51 @@ import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOpera * @author Fil Makarov - */ -contract SmartContractOwnershipRegistryModule is BaseAuthorizationModule { +contract SmartContractOwnershipRegistryModule is + BaseAuthorizationModule, + ISmartContractOwnershipRegistryModule +{ using ECDSA for bytes32; string public constant NAME = "Smart Contract Ownership Registry Module"; string public constant VERSION = "0.1.0"; mapping(address => address) internal _smartAccountOwners; - event OwnershipTransferred( - address indexed smartAccount, - address indexed oldOwner, - address indexed newOwner - ); - - error NoOwnerRegisteredForSmartAccount(address smartAccount); - error AlreadyInitedForSmartAccount(address smartAccount); - error WrongSignatureLength(); - error NotSmartContract(address account); - - /** - * @dev Initializes the module for a Smart Account. - * @dev no need to check for address(0) as it is not a Smart Contract - * Should be used at a time of first enabling the module for a Smart Account. - * @param owner The owner of the Smart Account. - */ - function initForSmartAccount(address owner) external returns (address) { - if (_smartAccountOwners[msg.sender] != address(0)) + /// @inheritdoc ISmartContractOwnershipRegistryModule + function initForSmartAccount( + address owner + ) external override returns (address) { + if (_smartAccountOwners[msg.sender] != address(0)) { revert AlreadyInitedForSmartAccount(msg.sender); + } if (!_isSmartContract(owner)) revert NotSmartContract(owner); _smartAccountOwners[msg.sender] = owner; return address(this); } - /** - * @dev Sets/changes an for a Smart Account. - * @dev no need to check for address(0) as it is not a Smart Contract - * Should be called by Smart Account itself. - * @param owner The owner of the Smart Account. - */ - function transferOwnership(address owner) external { + /// @inheritdoc ISmartContractOwnershipRegistryModule + function transferOwnership(address owner) external override { if (!_isSmartContract(owner)) revert NotSmartContract(owner); _transferOwnership(msg.sender, owner); } - /** - * @dev Renounces ownership - * should be called by Smart Account. - */ - function renounceOwnership() external { + /// @inheritdoc ISmartContractOwnershipRegistryModule + function renounceOwnership() external override { _transferOwnership(msg.sender, address(0)); } - /** - * @dev Returns the owner of the Smart Account. Reverts for Smart Accounts without owners. - * @param smartAccount Smart Account address. - * @return owner The owner of the Smart Account. - */ - function getOwner(address smartAccount) external view returns (address) { + /// @inheritdoc ISmartContractOwnershipRegistryModule + function getOwner( + address smartAccount + ) external view override returns (address) { address owner = _smartAccountOwners[smartAccount]; - if (owner == address(0)) + if (owner == address(0)) { revert NoOwnerRegisteredForSmartAccount(smartAccount); + } return owner; } - /** - * @dev validates userOperation - * @param userOp User Operation to be validated. - * @param userOpHash Hash of the User Operation to be validated. - * @return sigValidationResult 0 if signature is valid, SIG_VALIDATION_FAILED otherwise. - */ + /// @inheritdoc IAuthorizationModule function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash @@ -110,13 +90,7 @@ contract SmartContractOwnershipRegistryModule is BaseAuthorizationModule { return SIG_VALIDATION_FAILED; } - /** - * @dev Validates a signature for a message. - * To be called from a Smart Account. - * @param dataHash Exact hash of the data that was signed. - * @param moduleSignature Signature to be validated. - * @return EIP1271_MAGIC_VALUE if signature is valid, 0xffffffff otherwise. - */ + /// @inheritdoc ISignatureValidator function isValidSignature( bytes32 dataHash, bytes memory moduleSignature @@ -125,18 +99,12 @@ contract SmartContractOwnershipRegistryModule is BaseAuthorizationModule { isValidSignatureForAddress(dataHash, moduleSignature, msg.sender); } - /** - * @dev Validates a signature for a message signed by address. - * @param dataHash Exact hash of the data that was signed. - * @param moduleSignature Signature to be validated. - * @param smartAccount expected signer Smart Account address. - * @return EIP1271_MAGIC_VALUE if signature is valid, 0xffffffff otherwise. - */ + /// @inheritdoc ISmartContractOwnershipRegistryModule function isValidSignatureForAddress( bytes32 dataHash, bytes memory moduleSignature, address smartAccount - ) public view virtual returns (bytes4) { + ) public view virtual override returns (bytes4) { if (_verifySignature(dataHash, moduleSignature, smartAccount)) { return EIP1271_MAGIC_VALUE; } @@ -171,8 +139,9 @@ contract SmartContractOwnershipRegistryModule is BaseAuthorizationModule { address smartAccount ) internal view returns (bool) { address expectedContractSigner = _smartAccountOwners[smartAccount]; - if (expectedContractSigner == address(0)) + if (expectedContractSigner == address(0)) { revert NoOwnerRegisteredForSmartAccount(smartAccount); + } return ISignatureValidator(expectedContractSigner).isValidSignature( dataHash, diff --git a/contracts/smart-account/paymasters/BasePaymaster.sol b/contracts/smart-account/paymasters/BasePaymaster.sol deleted file mode 100644 index 18b6a6a50..000000000 --- a/contracts/smart-account/paymasters/BasePaymaster.sol +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.17; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {IPaymaster} from "@account-abstraction/contracts/interfaces/IPaymaster.sol"; -import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; -import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; -import {BaseSmartAccountErrors} from "../common/Errors.sol"; -import "@account-abstraction/contracts/core/Helpers.sol"; - -/** - * Helper class for creating a paymaster. - * provides helper methods for staking. - * validates that the postOp is called only by the ENTRY_POINT - @notice Could have Ownable2Step - */ -abstract contract BasePaymaster is IPaymaster, Ownable, BaseSmartAccountErrors { - IEntryPoint public immutable ENTRY_POINT; - - constructor(address _owner, IEntryPoint _entryPoint) { - ENTRY_POINT = _entryPoint; - _transferOwnership(_owner); - } - - /** - * add a deposit for this paymaster, used for paying for transaction fees - */ - function deposit() external payable virtual; - - /// @inheritdoc IPaymaster - function postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost - ) external override { - _requireFromEntryPoint(); - _postOp(mode, context, actualGasCost); - } - - /// @inheritdoc IPaymaster - function validatePaymasterUserOp( - UserOperation calldata userOp, - bytes32 userOpHash, - uint256 maxCost - ) external override returns (bytes memory context, uint256 validationData) { - _requireFromEntryPoint(); - return _validatePaymasterUserOp(userOp, userOpHash, maxCost); - } - - /** - * withdraw value from the deposit - * @param withdrawAddress target to send to - * @param amount to withdraw - */ - function withdrawTo( - address payable withdrawAddress, - uint256 amount - ) external virtual; - - /** - * add stake for this paymaster. - * This method can also carry eth value to add to the current stake. - * @param unstakeDelaySec - the unstake delay for this paymaster. Can only be increased. - */ - function addStake(uint32 unstakeDelaySec) external payable onlyOwner { - ENTRY_POINT.addStake{value: msg.value}(unstakeDelaySec); - } - - /** - * unlock the stake, in order to withdraw it. - * The paymaster can't serve requests once unlocked, until it calls addStake again - */ - function unlockStake() external onlyOwner { - ENTRY_POINT.unlockStake(); - } - - /** - * withdraw the entire paymaster's stake. - * stake must be unlocked first (and then wait for the unstakeDelay to be over) - * @param withdrawAddress the address to send withdrawn value. - */ - function withdrawStake(address payable withdrawAddress) external onlyOwner { - ENTRY_POINT.withdrawStake(withdrawAddress); - } - - /** - * return current paymaster's deposit on the ENTRY_POINT. - */ - function getDeposit() public view returns (uint256) { - return ENTRY_POINT.balanceOf(address(this)); - } - - function _validatePaymasterUserOp( - UserOperation calldata userOp, - bytes32 userOpHash, - uint256 maxCost - ) internal virtual returns (bytes memory context, uint256 validationData); - - /** - * post-operation handler. - * (verified to be called only through the ENTRY_POINT) - * @dev if subclass returns a non-empty context from validatePaymasterUserOp, it must also implement this method. - * @param mode enum with the following options: - * opSucceeded - user operation succeeded. - * opReverted - user op reverted. still has to pay for gas. - * postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert. - * Now this is the 2nd call, after user's op was deliberately reverted. - * @param context - the context value returned by validatePaymasterUserOp - * @param actualGasCost - actual gas used so far (without this postOp call). - */ - function _postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost - ) internal virtual { - (mode, context, actualGasCost); // unused params - // subclass must override this method if validatePaymasterUserOp returns a context - revert("must override"); - } - - /// validate the call is made from a valid entrypoint - function _requireFromEntryPoint() internal virtual { - if (msg.sender != address(ENTRY_POINT)) - revert CallerIsNotAnEntryPoint(msg.sender); - } -} diff --git a/contracts/smart-account/paymasters/PaymasterHelpers.sol b/contracts/smart-account/paymasters/PaymasterHelpers.sol deleted file mode 100644 index a5f31f2bc..000000000 --- a/contracts/smart-account/paymasters/PaymasterHelpers.sol +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.17; - -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; - -struct PaymasterData { - address paymasterId; - uint48 validUntil; - uint48 validAfter; - bytes signature; - uint256 signatureLength; -} - -struct PaymasterContext { - address paymasterId; - // could add maxFeePerGas and maxPriorityFeePerGas if needed - // by making approprate changes in paymaster contract -} - -/** - * @title PaymasterHelpers - helper functions for paymasters - */ -library PaymasterHelpers { - using ECDSA for bytes32; - - /** - * @dev Encodes the paymaster context: paymasterId and gasPrice - * @param data PaymasterData passed - */ - function paymasterContext( - PaymasterData memory data - ) - internal - pure - returns ( - // Could add maxFeePerGas and maxPriorityFeePerGas if needed - bytes memory context - ) - { - return abi.encode(data.paymasterId); - } - - /** - * @dev Decodes paymaster data assuming it follows PaymasterData - */ - function decodePaymasterData( - UserOperation calldata op - ) internal pure returns (PaymasterData memory) { - bytes calldata paymasterAndData = op.paymasterAndData; - ( - address paymasterId, - uint48 validUntil, - uint48 validAfter, - bytes memory signature - ) = abi.decode(paymasterAndData[20:], (address, uint48, uint48, bytes)); - return - PaymasterData( - paymasterId, - validUntil, - validAfter, - signature, - signature.length - ); - } - - /** - * @dev Decodes paymaster context assuming it follows PaymasterContext - */ - function decodePaymasterContext( - bytes memory context - ) internal pure returns (PaymasterContext memory) { - address paymasterId = abi.decode(context, (address)); - return PaymasterContext(paymasterId); - } -} diff --git a/contracts/smart-account/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol b/contracts/smart-account/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol deleted file mode 100644 index 4ae64959c..000000000 --- a/contracts/smart-account/paymasters/verifying/singleton/VerifyingSingletonPaymaster.sol +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.17; - -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import {UserOperation, UserOperationLib} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; -import "../../BasePaymaster.sol"; -import {PaymasterHelpers, PaymasterData, PaymasterContext} from "../../PaymasterHelpers.sol"; -import {SingletonPaymasterErrors} from "../../../common/Errors.sol"; - -/** - * @title A sample paymaster that uses external service to decide whether to pay for the UserOp. - * @dev The paymaster trusts an external signer to sign the transaction. - * The calling user must pass the UserOp to that external signer first, which performs whatever - * off-chain verification before signing the UserOp. - * @notice That this signature is NOT a replacement for wallet signature: - * - The paymaster signs to agree to PAY for GAS. - * - The wallet signs to prove identity and wallet ownership. - */ -contract VerifyingSingletonPaymaster is - BasePaymaster, - ReentrancyGuard, - SingletonPaymasterErrors -{ - using ECDSA for bytes32; - using UserOperationLib for UserOperation; - using PaymasterHelpers for UserOperation; - using PaymasterHelpers for bytes; - using PaymasterHelpers for PaymasterData; - - // Gas used in EntryPoint._handlePostOp() method (including this#postOp() call) - uint256 private _unaccountedEPGasOverhead; - mapping(address => uint256) public paymasterIdBalances; - - address public verifyingSigner; - - event EPGasOverheadChanged( - uint256 indexed _oldValue, - uint256 indexed _newValue - ); - - event VerifyingSignerChanged( - address indexed _oldSigner, - address indexed _newSigner, - address indexed _actor - ); - event GasDeposited(address indexed _paymasterId, uint256 indexed _value); - event GasWithdrawn( - address indexed _paymasterId, - address indexed _to, - uint256 indexed _value - ); - event GasBalanceDeducted( - address indexed _paymasterId, - uint256 indexed _charge - ); - - constructor( - address _owner, - IEntryPoint _entryPoint, - address _verifyingSigner - ) payable BasePaymaster(_owner, _entryPoint) { - if (address(_entryPoint) == address(0)) revert EntryPointCannotBeZero(); - if (_verifyingSigner == address(0)) - revert VerifyingSignerCannotBeZero(); - assembly { - sstore(verifyingSigner.slot, _verifyingSigner) - } - _unaccountedEPGasOverhead = 9600; - } - - /** - * @dev Deposit funds for a given paymasterId to cover transaction fees. - * @param paymasterId Identifier of the dapp receiving the deposit. - */ - function depositFor(address paymasterId) external payable nonReentrant { - if (paymasterId == address(0)) revert PaymasterIdCannotBeZero(); - if (msg.value == 0) revert DepositCanNotBeZero(); - paymasterIdBalances[paymasterId] = - paymasterIdBalances[paymasterId] + - msg.value; - ENTRY_POINT.depositTo{value: msg.value}(address(this)); - emit GasDeposited(paymasterId, msg.value); - } - - /** - * @dev Set a new verifying signer address. - * Can only be called by the owner of the contract. - * @param _newVerifyingSigner The new address to be set as the verifying signer. - * @notice If _newVerifyingSigner is set to zero address, it will revert with an error. - * After setting the new signer address, it will emit an event VerifyingSignerChanged. - */ - function setSigner(address _newVerifyingSigner) external payable onlyOwner { - if (_newVerifyingSigner == address(0)) - revert VerifyingSignerCannotBeZero(); - address oldSigner = verifyingSigner; - assembly { - sstore(verifyingSigner.slot, _newVerifyingSigner) - } - emit VerifyingSignerChanged(oldSigner, _newVerifyingSigner, msg.sender); - } - - function setUnaccountedEPGasOverhead(uint256 value) external onlyOwner { - uint256 oldValue = _unaccountedEPGasOverhead; - _unaccountedEPGasOverhead = value; - emit EPGasOverheadChanged(oldValue, value); - } - - /** - * @dev get the current deposit for paymasterId (Dapp Depositor address) - * @param paymasterId dapp identifier - */ - function getBalance( - address paymasterId - ) external view returns (uint256 balance) { - balance = paymasterIdBalances[paymasterId]; - } - - /** - @dev Override the default implementation. - */ - function deposit() public payable virtual override { - revert("user DepositFor instead"); - } - - /** - * @dev Withdraws specified gas tokens from paymaster's balance to a given address. - * @param withdrawAddress Address receiving the gas tokens. - * @param amount Amount of gas tokens to withdraw. - */ - function withdrawTo( - address payable withdrawAddress, - uint256 amount - ) public override nonReentrant { - if (withdrawAddress == address(0)) revert CanNotWithdrawToZeroAddress(); - uint256 currentBalance = paymasterIdBalances[msg.sender]; - if (amount > currentBalance) - revert InsufficientBalance(amount, currentBalance); - paymasterIdBalances[msg.sender] = - paymasterIdBalances[msg.sender] - - amount; - ENTRY_POINT.withdrawTo(withdrawAddress, amount); - emit GasWithdrawn(msg.sender, withdrawAddress, amount); - } - - /** - * @dev Called by off-chain service for signing, and on-chain in validatePaymasterUserOp for validation. - * @notice Signature covers all UserOperation fields except "paymasterAndData" which carries the signature. - * @return Hash to sign off-chain and validate on-chain. - */ - function getHash( - UserOperation calldata userOp, - address paymasterId, - uint48 validUntil, - uint48 validAfter - ) public view returns (bytes32) { - // can't use userOp.hash(), since it contains also the paymasterAndData itself. - address sender = userOp.getSender(); - return - keccak256( - abi.encode( - sender, - userOp.nonce, - keccak256(userOp.initCode), - keccak256(userOp.callData), - userOp.callGasLimit, - userOp.verificationGasLimit, - userOp.preVerificationGas, - userOp.maxFeePerGas, - userOp.maxPriorityFeePerGas, - block.chainid, - address(this), - paymasterId, - validUntil, - validAfter - ) - ); - } - - /** - * @dev Executes the paymaster's payment conditions - * @param mode tells whether the op succeeded, reverted, or if the op succeeded but cause the postOp to revert - * @param context payment conditions signed by the paymaster in `validatePaymasterUserOp` - * @param actualGasCost amount to be paid to the entry point in wei - */ - function _postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost - ) internal virtual override { - (mode); - PaymasterContext memory data = context.decodePaymasterContext(); - address extractedPaymasterId = data.paymasterId; - uint256 balToDeduct = actualGasCost + - _unaccountedEPGasOverhead * - tx.gasprice; - paymasterIdBalances[extractedPaymasterId] = - paymasterIdBalances[extractedPaymasterId] - - balToDeduct; - emit GasBalanceDeducted(extractedPaymasterId, balToDeduct); - } - - /** - * @dev Verify that an external signer signed the paymaster data of a user operation. - * The paymaster data is expected to be the paymaster and a signature over the entire request parameters. - * @param userOp The UserOperation struct that represents the current user operation. - * userOpHash The hash of the UserOperation struct. - * @param requiredPreFund The required amount of pre-funding for the paymaster. - * @return context A context string returned by the entry point after successful validation. - * @return validationData An integer returned by the entry point after successful validation. - */ - function _validatePaymasterUserOp( - UserOperation calldata userOp, - bytes32 /*userOpHash*/, - uint256 requiredPreFund - ) - internal - view - override - returns (bytes memory context, uint256 validationData) - { - PaymasterData memory paymasterData = userOp.decodePaymasterData(); - bytes32 hash = getHash( - userOp, - paymasterData.paymasterId, - paymasterData.validUntil, - paymasterData.validAfter - ); - uint256 sigLength = paymasterData.signatureLength; - // Ensure revert reason is from "VerifyingPaymaster" not "ECDSA" on invalid signature. - - if (sigLength != 65) revert InvalidPaymasterSignatureLength(sigLength); - // Don't revert on signature failure: return SIG_VALIDATION_FAILED. - if ( - verifyingSigner != - hash.toEthSignedMessageHash().recover(paymasterData.signature) - ) { - // Empty context and sigFailed with time range provided - return ( - "", - _packValidationData( - true, - paymasterData.validUntil, - paymasterData.validAfter - ) - ); - } - if (requiredPreFund > paymasterIdBalances[paymasterData.paymasterId]) - revert InsufficientBalance( - requiredPreFund, - paymasterIdBalances[paymasterData.paymasterId] - ); - return ( - PaymasterHelpers.paymasterContext(paymasterData), - _packValidationData( - false, - paymasterData.validUntil, - paymasterData.validAfter - ) - ); - } -} diff --git a/contracts/smart-account/test/mocks/MockAuthModule.sol b/contracts/smart-account/test/mocks/MockAuthModule.sol index 17c861252..3d3eacb29 100644 --- a/contracts/smart-account/test/mocks/MockAuthModule.sol +++ b/contracts/smart-account/test/mocks/MockAuthModule.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.17; import {BaseAuthorizationModule} from "../../modules/BaseAuthorizationModule.sol"; import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; +import {EIP1271_MAGIC_VALUE} from "contracts/smart-account/interfaces/ISignatureValidator.sol"; contract MockAuthModule is BaseAuthorizationModule { mapping(address => bytes) internal setupData; diff --git a/contracts/smart-account/test/mocks/MockInvalidInitialAuthModule2.sol b/contracts/smart-account/test/mocks/MockInvalidInitialAuthModule2.sol index 92311a88c..2e4a57b2a 100644 --- a/contracts/smart-account/test/mocks/MockInvalidInitialAuthModule2.sol +++ b/contracts/smart-account/test/mocks/MockInvalidInitialAuthModule2.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.17; import {BaseAuthorizationModule} from "../../modules/BaseAuthorizationModule.sol"; import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; +import {EIP1271_MAGIC_VALUE} from "contracts/smart-account/interfaces/ISignatureValidator.sol"; contract MockInvalidInitialAuthModule is BaseAuthorizationModule { mapping(address => bytes) internal _setupData; diff --git a/contracts/smart-account/test/mocks/MockProtocol/MockProtocolSVModule.sol b/contracts/smart-account/test/mocks/MockProtocol/MockProtocolSVModule.sol index 218f1b30d..02d81a59f 100644 --- a/contracts/smart-account/test/mocks/MockProtocol/MockProtocolSVModule.sol +++ b/contracts/smart-account/test/mocks/MockProtocol/MockProtocolSVModule.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import "../../../modules/SessionValidationModules/ISessionValidationModule.sol"; +import "../../../interfaces/modules/ISessionValidationModule.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; contract MockProtocolSVM is ISessionValidationModule { diff --git a/contracts/smart-account/test/mocks/MockSessionValidationModule.sol b/contracts/smart-account/test/mocks/MockSessionValidationModule.sol index f1e800bca..22c4b18f3 100644 --- a/contracts/smart-account/test/mocks/MockSessionValidationModule.sol +++ b/contracts/smart-account/test/mocks/MockSessionValidationModule.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.17; -import {ISessionValidationModule} from "../../modules/SessionValidationModules/ISessionValidationModule.sol"; +import {ISessionValidationModule} from "../../interfaces/modules/ISessionValidationModule.sol"; import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/contracts/smart-account/test/upgrades/BaseSmartAccountNew.sol b/contracts/smart-account/test/upgrades/BaseSmartAccountNew.sol index 983f86633..5e17690c8 100644 --- a/contracts/smart-account/test/upgrades/BaseSmartAccountNew.sol +++ b/contracts/smart-account/test/upgrades/BaseSmartAccountNew.sol @@ -9,7 +9,6 @@ import {IAccount} from "@account-abstraction/contracts/interfaces/IAccount.sol"; import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; import {UserOperationLib, UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; import {Enum} from "../../common/Enum.sol"; -import {BaseSmartAccountErrors} from "../../common/Errors.sol"; import "@account-abstraction/contracts/core/Helpers.sol"; struct Transaction { @@ -33,7 +32,9 @@ struct FeeRefund { * this contract provides the basic logic for implementing the IAccount interface - validateUserOp * specific account implementation should inherit it and provide the account-specific logic */ -abstract contract BaseSmartAccountNew is IAccount, BaseSmartAccountErrors { +abstract contract BaseSmartAccountNew is IAccount { + error CallerIsNotAnEntryPoint(address caller); + using UserOperationLib for UserOperation; //return value in case of signature failure, with no time-range. diff --git a/contracts/smart-account/test/upgrades/v1/FallbackManagerV1.sol b/contracts/smart-account/test/upgrades/v1/FallbackManagerV1.sol index bc9561466..b46513ba3 100644 --- a/contracts/smart-account/test/upgrades/v1/FallbackManagerV1.sol +++ b/contracts/smart-account/test/upgrades/v1/FallbackManagerV1.sol @@ -2,23 +2,18 @@ pragma solidity 0.8.17; import {SelfAuthorized} from "../../../common/SelfAuthorized.sol"; -import {FallbackManagerErrors} from "../../../common/Errors.sol"; +import {IFallbackManager} from "contracts/smart-account/interfaces/base/IFallbackManager.sol"; /** * @title Fallback Manager - A contract that manages fallback calls made to the Smart Account * @dev Fallback calls are handled by a `handler` contract that is stored at FALLBACK_HANDLER_STORAGE_SLOT * fallback calls are not delegated to the `handler` so they can not directly change Smart Account storage */ -abstract contract FallbackManagerV1 is SelfAuthorized, FallbackManagerErrors { +abstract contract FallbackManagerV1 is SelfAuthorized, IFallbackManager { // keccak-256 hash of "fallback_manager.handler.address" subtracted by 1 bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d4; - event ChangedFallbackHandler( - address indexed previousHandler, - address indexed handler - ); - // solhint-disable-next-line payable-fallback,no-complex-fallback fallback() external { bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT; diff --git a/contracts/smart-account/test/upgrades/v1/SmartAccountV1.sol b/contracts/smart-account/test/upgrades/v1/SmartAccountV1.sol index 76074f626..c71bcec19 100644 --- a/contracts/smart-account/test/upgrades/v1/SmartAccountV1.sol +++ b/contracts/smart-account/test/upgrades/v1/SmartAccountV1.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {BaseSmartAccount, IEntryPoint, Transaction, FeeRefund, Enum, UserOperation} from "./BaseSmartAccountV1.sol"; import {ModuleManagerV1} from "./ModuleManagerV1.sol"; import {FallbackManagerV1} from "./FallbackManagerV1.sol"; @@ -8,12 +9,12 @@ import {SignatureDecoder} from "../../../common/SignatureDecoder.sol"; import {SecuredTokenTransfer} from "../../../common/SecuredTokenTransfer.sol"; import {LibAddress} from "../../../libs/LibAddress.sol"; import {ISignatureValidator} from "../../../interfaces/ISignatureValidator.sol"; -import {Math} from "../../../libs/Math.sol"; -import {IERC165} from "../../../interfaces/IERC165.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {ReentrancyGuard} from "../../../common/ReentrancyGuard.sol"; import {SmartAccountErrorsV1} from "./ErrorsV1.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IModule} from "./IModuleV1.sol"; +import {EIP1271_MAGIC_VALUE} from "contracts/smart-account/interfaces/ISignatureValidator.sol"; /** * @title SmartAccount - EIP-4337 compatible smart contract wallet. diff --git a/contracts/smart-account/test/vulnerable/VulnerableERC20SessionValidationModule.sol b/contracts/smart-account/test/vulnerable/VulnerableERC20SessionValidationModule.sol index 402b911f1..78894ba46 100644 --- a/contracts/smart-account/test/vulnerable/VulnerableERC20SessionValidationModule.sol +++ b/contracts/smart-account/test/vulnerable/VulnerableERC20SessionValidationModule.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import "../../modules/SessionValidationModules/ISessionValidationModule.sol"; +import "../../interfaces/modules/ISessionValidationModule.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; /** diff --git a/test/bundler-integration/module/BatchedSessionRouter.Module.specs.ts b/test/bundler-integration/module/BatchedSessionRouter.Module.specs.ts index 1ffca91d3..c907528d2 100644 --- a/test/bundler-integration/module/BatchedSessionRouter.Module.specs.ts +++ b/test/bundler-integration/module/BatchedSessionRouter.Module.specs.ts @@ -12,7 +12,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../utils/setupHelper"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { BundlerTestEnvironment } from "../environment/bundlerEnvironment"; @@ -168,10 +167,6 @@ describe("SessionKey: Session Router (via Bundler)", async () => { ecdsaModule: ecdsaModule, userSA: userSA, mockToken: mockToken, - verifyingPaymaster: await getVerifyingPaymaster( - deployer, - verifiedSigner - ), sessionKeyManager: sessionKeyManager, erc20SessionModule: erc20SessionModule, sessionKeyData: sessionKeyData, diff --git a/test/bundler-integration/module/MultichainValidator.Module.specs.ts b/test/bundler-integration/module/MultichainValidator.Module.specs.ts index 68f78c2a9..c1cb0f35d 100644 --- a/test/bundler-integration/module/MultichainValidator.Module.specs.ts +++ b/test/bundler-integration/module/MultichainValidator.Module.specs.ts @@ -13,7 +13,6 @@ import { getSmartAccountImplementation, getMockToken, getStakedSmartAccountFactory, - getVerifyingPaymaster, } from "../../utils/setupHelper"; import { keccak256 } from "ethereumjs-util"; import { MerkleTree } from "merkletreejs"; @@ -234,7 +233,6 @@ describe("MultichainValidator Module", async () => { smartAccountFactory: smartAccountFactory, mockToken: mockToken, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), multichainECDSAValidator: multichainECDSAValidator, sessionKeyManager: sessionKeyManager, sessionKeyMerkleTree: sessionKeyMerkleTree, diff --git a/test/bundler-integration/module/SessionKeyManager.Module.specs.ts b/test/bundler-integration/module/SessionKeyManager.Module.specs.ts index 910dbea19..a8258cd36 100644 --- a/test/bundler-integration/module/SessionKeyManager.Module.specs.ts +++ b/test/bundler-integration/module/SessionKeyManager.Module.specs.ts @@ -14,7 +14,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../utils/setupHelper"; import { keccak256 } from "ethereumjs-util"; import { MerkleTree } from "merkletreejs"; @@ -127,7 +126,6 @@ describe("SessionKey: SessionKey Manager Module (with Bundler)", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), sessionKeyManager: sessionKeyManager, mockSessionValidationModule: mockSessionValidationModule, sessionKeyData: sessionKeyData, diff --git a/test/bundler-integration/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts b/test/bundler-integration/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts index 688621f7b..eb1cdd865 100644 --- a/test/bundler-integration/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts +++ b/test/bundler-integration/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts @@ -15,7 +15,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../../utils/setupHelper"; import { BigNumber } from "ethers"; import { UserOperation } from "../../../utils/userOperation"; @@ -136,7 +135,6 @@ describe("SessionKey: ERC20 Session Validation Module (with Bundler)", async () ecdsaModule: ecdsaModule, userSA: userSA, mockToken: mockToken, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), sessionKeyManager: sessionKeyManager, erc20SessionModule: erc20SessionModule, sessionKeyData: sessionKeyData, diff --git a/test/bundler-integration/smart-account/SA.Basics.specs.ts b/test/bundler-integration/smart-account/SA.Basics.specs.ts index a0ae7e043..0f3a0fd7a 100644 --- a/test/bundler-integration/smart-account/SA.Basics.specs.ts +++ b/test/bundler-integration/smart-account/SA.Basics.specs.ts @@ -8,7 +8,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../utils/setupHelper"; import { makeEcdsaModuleUserOp, @@ -86,7 +85,6 @@ describe("Modular Smart Account Basics (with Bundler)", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); @@ -144,45 +142,4 @@ describe("Modular Smart Account Basics (with Bundler)", async () => { charlieBalanceBefore.add(amountToTransfer) ); }); - - // TODO: This test fails with the message paymaster uses banned opcode: BASEFEE - it("Can send a userOp with Paymaster payment", async () => { - const { entryPoint, mockToken, userSA, ecdsaModule, verifyingPaymaster } = - await setupTests(); - - const charlieTokenBalanceBefore = await mockToken.balanceOf( - charlie.address - ); - const tokenAmountToTransfer = ethers.utils.parseEther("0.6458"); - - const blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - const validUntil = blockTimestamp + 1000; - const validAfter = blockTimestamp; - - const userOp = await makeEcdsaModuleUserOpWithPaymaster( - "execute_ncC", - [ - mockToken.address, - ethers.utils.parseEther("0"), - encodeTransfer(charlie.address, tokenAmountToTransfer.toString()), - ], - userSA.address, - smartAccountOwner, - entryPoint, - ecdsaModule.address, - verifyingPaymaster, - verifiedSigner, - validUntil, - validAfter, - { - preVerificationGas: 50000, - } - ); - - await environment.sendUserOperation(userOp, entryPoint.address); - - expect(await mockToken.balanceOf(charlie.address)).to.equal( - charlieTokenBalanceBefore.add(tokenAmountToTransfer) - ); - }); }); diff --git a/test/bundler-integration/smart-account/SA.Modules.specs.ts b/test/bundler-integration/smart-account/SA.Modules.specs.ts index 62b81e494..bfb0618af 100644 --- a/test/bundler-integration/smart-account/SA.Modules.specs.ts +++ b/test/bundler-integration/smart-account/SA.Modules.specs.ts @@ -7,7 +7,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../utils/setupHelper"; import { makeEcdsaModuleUserOp } from "../../utils/userOp"; import { BundlerTestEnvironment } from "../environment/bundlerEnvironment"; @@ -87,7 +86,6 @@ describe("Modular Smart Account Modules (with Bundler)", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); diff --git a/test/bundler-integration/smart-account/SA.Setup.specs.ts b/test/bundler-integration/smart-account/SA.Setup.specs.ts index 4366dbe66..5a008c9fd 100644 --- a/test/bundler-integration/smart-account/SA.Setup.specs.ts +++ b/test/bundler-integration/smart-account/SA.Setup.specs.ts @@ -7,7 +7,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, deployContract, } from "../../utils/setupHelper"; import { makeEcdsaModuleUserOp } from "../../utils/userOp"; @@ -84,7 +83,6 @@ describe("Smart Account Setup (with Bundler)", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); diff --git a/test/bundler-integration/smart-account/SA.UserOps.specs.ts b/test/bundler-integration/smart-account/SA.UserOps.specs.ts index 739b27530..9f32a28ee 100644 --- a/test/bundler-integration/smart-account/SA.UserOps.specs.ts +++ b/test/bundler-integration/smart-account/SA.UserOps.specs.ts @@ -8,7 +8,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../utils/setupHelper"; import { makeEcdsaModuleUserOp } from "../../utils/userOp"; import { BundlerTestEnvironment } from "../environment/bundlerEnvironment"; @@ -84,7 +83,6 @@ describe("UserOps (with Bundler)", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); diff --git a/test/bundler-integration/smart-account/SA.concurrent.txn.specs.ts b/test/bundler-integration/smart-account/SA.concurrent.txn.specs.ts index 6dabcd561..839e4c152 100644 --- a/test/bundler-integration/smart-account/SA.concurrent.txn.specs.ts +++ b/test/bundler-integration/smart-account/SA.concurrent.txn.specs.ts @@ -8,13 +8,8 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../utils/setupHelper"; -import { - makeEcdsaModuleUserOp, - makeEcdsaModuleUserOp2D, - makeEcdsaModuleUserOpWithPaymaster, -} from "../../utils/userOp"; +import { makeEcdsaModuleUserOp } from "../../utils/userOp"; import { BundlerTestEnvironment } from "../environment/bundlerEnvironment"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { UserOperation } from "../../utils/userOperation"; @@ -100,10 +95,6 @@ describe("Modular Smart Account Basics (with Bundler)", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster( - deployer, - verifiedSigner - ), }; } ); diff --git a/test/bundler-integration/upgrades/v1-to-v2/v1-to-v2-upgrade.specs.ts b/test/bundler-integration/upgrades/v1-to-v2/v1-to-v2-upgrade.specs.ts index 5e49302b8..9bab3d833 100644 --- a/test/bundler-integration/upgrades/v1-to-v2/v1-to-v2-upgrade.specs.ts +++ b/test/bundler-integration/upgrades/v1-to-v2/v1-to-v2-upgrade.specs.ts @@ -8,7 +8,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../../utils/setupHelper"; import { fillAndSign, makeEcdsaModuleUserOp } from "../../../utils/userOp"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; @@ -121,7 +120,6 @@ describe("Upgrade v1 to Modular (v2) (ex. Ownerless) (with Bundler)", async () = ecdsaModule: ecdsaModule, userSA: userSA, userSAV1: userSAV1, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); @@ -133,7 +131,6 @@ describe("Upgrade v1 to Modular (v2) (ex. Ownerless) (with Bundler)", async () = mockToken, ecdsaModule, userSAV1, - verifyingPaymaster, } = await setupTests(); const EcdsaOwnershipRegistryModule = await ethers.getContractFactory( @@ -194,7 +191,6 @@ describe("Upgrade v1 to Modular (v2) (ex. Ownerless) (with Bundler)", async () = mockToken: mockToken, ecdsaModule: ecdsaModule, userSAModular: userSAModular, - verifyingPaymaster: verifyingPaymaster, }; }; diff --git a/test/gas-benchmarks/Basic-Operations.specs.ts b/test/gas-benchmarks/Basic-Operations.specs.ts index a802b3ec9..61b0e50ba 100644 --- a/test/gas-benchmarks/Basic-Operations.specs.ts +++ b/test/gas-benchmarks/Basic-Operations.specs.ts @@ -7,7 +7,6 @@ import { getSmartAccountFactory, getMockToken, getEcdsaOwnershipRegistryModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; import { makeEcdsaModuleUserOp, @@ -140,7 +139,6 @@ describe("Gas Benchmarking. Basic operations", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); @@ -315,49 +313,4 @@ describe("Gas Benchmarking. Basic operations", async () => { charlieTokenBalanceBefore.add(tokenAmountToTransfer) ); }); - - it("Can send a userOp with Paymaster payment", async () => { - const { entryPoint, mockToken, userSA, ecdsaModule, verifyingPaymaster } = - await setupTests(); - - const charlieTokenBalanceBefore = await mockToken.balanceOf( - charlie.address - ); - const tokenAmountToTransfer = ethers.utils.parseEther("0.6458"); - - const blockTimestamp = (await ethers.provider.getBlock("latest")).timestamp; - const validUntil = blockTimestamp + 1000; - const validAfter = blockTimestamp; - - const userOp = await makeEcdsaModuleUserOpWithPaymaster( - "execute_ncC", - [ - mockToken.address, - ethers.utils.parseEther("0"), - encodeTransfer(charlie.address, tokenAmountToTransfer.toString()), - ], - userSA.address, - smartAccountOwner, - entryPoint, - ecdsaModule.address, - verifyingPaymaster, - verifiedSigner, - validUntil, - validAfter - ); - - const handleOpsTxn = await entryPoint.handleOps( - [userOp], - verifiedSigner.address - ); - const receipt = await handleOpsTxn.wait(); - console.log( - "UserOp ERC20 Token transfer with Paymaster gas used: ", - receipt.gasUsed.toString() - ); - - expect(await mockToken.balanceOf(charlie.address)).to.equal( - charlieTokenBalanceBefore.add(tokenAmountToTransfer) - ); - }); }); diff --git a/test/misc/Create3-Deployer.specs.ts b/test/misc/Create3-Deployer.specs.ts index 5b3db5f20..42f43b4ec 100644 --- a/test/misc/Create3-Deployer.specs.ts +++ b/test/misc/Create3-Deployer.specs.ts @@ -56,40 +56,4 @@ describe("Deploy the deployer and then deploy more contracts using it", function deployerInstance.connect(anyDeployer).deploy(salt, entryPointBytecode) ).to.be.revertedWith("TargetAlreadyExists"); }); - - it("Deploys MultiSend", async function () { - const salt = ethers.utils.keccak256( - ethers.utils.toUtf8Bytes(DEPLOYMENT_SALTS_DEV.MULTI_SEND) - ); - - const provider = ethers.provider; - - const multiSend = await ethers.getContractFactory("MultiSend"); - const multiSendBytecode = `${multiSend.bytecode}`; - const multiSendComputedAddr = await deployerInstance.addressOf(salt); - - // console.log("MultiSend Computed Address: ", multiSendComputedAddr); - - const ismultiSendDeployed = await isContract( - multiSendComputedAddr, - provider - ); // true (deployed on-chain) - if (!ismultiSendDeployed) { - const code = await provider.getCode(multiSendComputedAddr); - // console.log("code before.. ", code); - expect(code).to.be.equal("0x"); - await deployerInstance - .connect(anyDeployer) - .deploy(salt, multiSendBytecode); - } - - // console.log("entrypoint deployed at: ", multiSendComputedAddr); - const code = await provider.getCode(multiSendComputedAddr); - // console.log("code after.. ", code); - expect(code).to.not.equal("0x"); - - await expect( - deployerInstance.connect(anyDeployer).deploy(salt, multiSendBytecode) - ).to.be.revertedWith("TargetAlreadyExists"); - }); }); diff --git a/test/module/BatchedSessionRouter.Module.specs.ts b/test/module/BatchedSessionRouter.Module.specs.ts index 71c3918cc..79398b35b 100644 --- a/test/module/BatchedSessionRouter.Module.specs.ts +++ b/test/module/BatchedSessionRouter.Module.specs.ts @@ -13,7 +13,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; import { computeAddress } from "ethers/lib/utils"; @@ -149,10 +148,6 @@ describe("SessionKey: Batched Session Router", async () => { ecdsaModule: ecdsaModule, userSA: userSA, mockToken: mockToken, - verifyingPaymaster: await getVerifyingPaymaster( - deployer, - verifiedSigner - ), sessionKeyManager: sessionKeyManager, erc20SessionModule: erc20SessionModule, sessionKeyData: sessionKeyData, diff --git a/test/module/ForwardFlow.Module.specs.ts b/test/module/ForwardFlow.Module.specs.ts index fecd03248..26125f330 100644 --- a/test/module/ForwardFlow.Module.specs.ts +++ b/test/module/ForwardFlow.Module.specs.ts @@ -19,7 +19,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; describe("Forward Flow Module", async () => { @@ -83,7 +82,6 @@ describe("Forward Flow Module", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); diff --git a/test/module/MultichainValidator.Module.specs.ts b/test/module/MultichainValidator.Module.specs.ts index 87516fd5f..ea57de4f4 100644 --- a/test/module/MultichainValidator.Module.specs.ts +++ b/test/module/MultichainValidator.Module.specs.ts @@ -14,7 +14,6 @@ import { getSmartAccountFactory, getMockToken, getEcdsaOwnershipRegistryModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; import { keccak256 } from "ethereumjs-util"; import { MerkleTree } from "merkletreejs"; @@ -239,7 +238,6 @@ describe("MultichainValidator Module", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), multichainECDSAValidator: multichainECDSAValidator, sessionKeyManager: sessionKeyManager, sessionKeyMerkleTree: sessionKeyMerkleTree, diff --git a/test/module/SessionKeyManager.Module.specs.ts b/test/module/SessionKeyManager.Module.specs.ts index 036ce6af8..e510375df 100644 --- a/test/module/SessionKeyManager.Module.specs.ts +++ b/test/module/SessionKeyManager.Module.specs.ts @@ -15,7 +15,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; import { keccak256 } from "ethereumjs-util"; import { MerkleTree } from "merkletreejs"; @@ -103,7 +102,6 @@ describe("SessionKey: SessionKey Manager Module", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), sessionKeyManager: sessionKeyManager, mockSessionValidationModule: mockSessionValidationModule, sessionKeyData: sessionKeyData, diff --git a/test/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts b/test/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts index 8e9c8d012..465307fce 100644 --- a/test/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts +++ b/test/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts @@ -15,7 +15,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../utils/setupHelper"; import { BigNumber } from "ethers"; import { UserOperation } from "../../utils/userOperation"; @@ -108,7 +107,6 @@ describe("SessionKey: ERC20 Session Validation Module", async () => { ecdsaModule: ecdsaModule, userSA: userSA, mockToken: mockToken, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), sessionKeyManager: sessionKeyManager, erc20SessionModule: erc20SessionModule, sessionKeyData: sessionKeyData, diff --git a/test/smart-account/SA.Basics.specs.ts b/test/smart-account/SA.Basics.specs.ts index fafc569c7..6326200d5 100644 --- a/test/smart-account/SA.Basics.specs.ts +++ b/test/smart-account/SA.Basics.specs.ts @@ -7,7 +7,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; describe("Modular Smart Account Basics: ", async () => { @@ -50,7 +49,6 @@ describe("Modular Smart Account Basics: ", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); diff --git a/test/smart-account/SA.Getters.specs.ts b/test/smart-account/SA.Getters.specs.ts index 6bde18aca..f94dbea3a 100644 --- a/test/smart-account/SA.Getters.specs.ts +++ b/test/smart-account/SA.Getters.specs.ts @@ -7,7 +7,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; describe("Smart Account Getters", async () => { @@ -53,10 +52,6 @@ describe("Smart Account Getters", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster( - deployer, - verifiedSigner - ), }; } ); diff --git a/test/smart-account/SA.Modules.specs.ts b/test/smart-account/SA.Modules.specs.ts index 7e8f6753c..050fd2ed0 100644 --- a/test/smart-account/SA.Modules.specs.ts +++ b/test/smart-account/SA.Modules.specs.ts @@ -7,7 +7,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; import { makeEcdsaModuleUserOp } from "../utils/userOp"; @@ -54,7 +53,6 @@ describe("Modular Smart Account Modules: ", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); diff --git a/test/smart-account/SA.Setup.specs.ts b/test/smart-account/SA.Setup.specs.ts index 45ad877d1..e35368036 100644 --- a/test/smart-account/SA.Setup.specs.ts +++ b/test/smart-account/SA.Setup.specs.ts @@ -7,7 +7,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; import { makeEcdsaModuleUserOp } from "../utils/userOp"; import { AddressZero } from "@ethersproject/constants"; @@ -54,7 +53,6 @@ describe("Smart Account Setup", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); diff --git a/test/smart-account/SA.UserOps.specs.ts b/test/smart-account/SA.UserOps.specs.ts index 7460b3615..2ab86143e 100644 --- a/test/smart-account/SA.UserOps.specs.ts +++ b/test/smart-account/SA.UserOps.specs.ts @@ -8,7 +8,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../utils/setupHelper"; import { makeEcdsaModuleUserOp, fillAndSign } from "../utils/userOp"; @@ -60,7 +59,6 @@ describe("UserOps", async () => { mockToken: mockToken, ecdsaModule: ecdsaModule, userSA: userSA, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); diff --git a/test/upgrades/v1-to-v2/v1-to-v2-upgrade.specs.ts b/test/upgrades/v1-to-v2/v1-to-v2-upgrade.specs.ts index d56447791..76a7df4c4 100644 --- a/test/upgrades/v1-to-v2/v1-to-v2-upgrade.specs.ts +++ b/test/upgrades/v1-to-v2/v1-to-v2-upgrade.specs.ts @@ -8,7 +8,6 @@ import { getMockToken, getEcdsaOwnershipRegistryModule, getSmartAccountWithModule, - getVerifyingPaymaster, } from "../../utils/setupHelper"; import { fillAndSign, makeEcdsaModuleUserOp } from "../../utils/userOp"; @@ -91,7 +90,6 @@ describe("Upgrade v1 to Modular (v2) (ex. Ownerless)", async () => { ecdsaModule: ecdsaModule, userSA: userSA, userSAV1: userSAV1, - verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner), }; }); diff --git a/test/utils/setupHelper.ts b/test/utils/setupHelper.ts index 89d56389b..e652a1d0b 100644 --- a/test/utils/setupHelper.ts +++ b/test/utils/setupHelper.ts @@ -75,35 +75,6 @@ export const getSmartContractOwnershipRegistryModule = async () => { ); }; -export const getVerifyingPaymaster = async ( - owner: Wallet | SignerWithAddress, - verifiedSigner: Wallet | SignerWithAddress -) => { - const entryPoint = await getEntryPoint(); - const VerifyingSingletonPaymaster = await hre.ethers.getContractFactory( - "VerifyingSingletonPaymaster" - ); - const verifyingSingletonPaymaster = await VerifyingSingletonPaymaster.deploy( - owner.address, - entryPoint.address, - verifiedSigner.address - ); - - await verifyingSingletonPaymaster - .connect(owner) - .addStake(10, { value: ethers.utils.parseEther("2") }); - - await verifyingSingletonPaymaster.depositFor(verifiedSigner.address, { - value: ethers.utils.parseEther("1"), - }); - - await entryPoint.depositTo(verifyingSingletonPaymaster.address, { - value: ethers.utils.parseEther("10"), - }); - - return verifyingSingletonPaymaster; -}; - export const getSmartAccountWithModule = async ( moduleSetupContract: string, moduleSetupData: BytesLike,