From 6f7ca6f120fab7883cbdb8396e0994eed84da8c5 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 10 Nov 2023 13:50:27 +0100 Subject: [PATCH 01/83] Update version number package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5234628..8f51e3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openfort/openfort-contracts", - "version": "0.4.0", + "version": "0.5.0", "description": "Official Contracts of the Openfort Project", "license": "GPL-3.0", "scripts": { From fb4f93d682ca6b89f5433362b034860f80f85106 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 10 Nov 2023 13:50:38 +0100 Subject: [PATCH 02/83] forge install: erc6551 v0.3.1 --- .gitmodules | 3 +++ lib/erc6551 | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/erc6551 diff --git a/.gitmodules b/.gitmodules index 54dff72..e1c89ea 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/account-abstraction"] path = lib/account-abstraction url = git@github.com:eth-infinitism/account-abstraction.git +[submodule "lib/erc6551"] + path = lib/erc6551 + url = https://github.com/erc6551/reference diff --git a/lib/erc6551 b/lib/erc6551 new file mode 160000 index 0000000..43a8457 --- /dev/null +++ b/lib/erc6551 @@ -0,0 +1 @@ +Subproject commit 43a84573bb47b0df3ab543a20365f4974f56a809 From f27f45467d505dca4e6cda78f28ebcccebb1d4a5 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 10 Nov 2023 14:05:40 +0100 Subject: [PATCH 03/83] Custom Registry not needed anymore --- contracts/core/eip6551/ERC6551Registry.sol | 62 ------------------- .../eip6551/interfaces/IERC6551Registry.sol | 36 ----------- 2 files changed, 98 deletions(-) delete mode 100644 contracts/core/eip6551/ERC6551Registry.sol delete mode 100644 contracts/core/eip6551/interfaces/IERC6551Registry.sol diff --git a/contracts/core/eip6551/ERC6551Registry.sol b/contracts/core/eip6551/ERC6551Registry.sol deleted file mode 100644 index 3c28f79..0000000 --- a/contracts/core/eip6551/ERC6551Registry.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; - -/* solhint-disable reason-string */ - -import "@openzeppelin/contracts/utils/Create2.sol"; -import "./interfaces/IERC6551Registry.sol"; - -contract ERC6551Registry is IERC6551Registry { - error InitializationFailed(); - - function createAccount( - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 salt, - bytes calldata initData - ) external returns (address) { - bytes memory code = _creationCode(implementation, chainId, tokenContract, tokenId, salt); - - address _account = Create2.computeAddress(bytes32(salt), keccak256(code)); - - if (_account.code.length != 0) return _account; - - _account = Create2.deploy(0, bytes32(salt), code); - - if (initData.length != 0) { - (bool success,) = _account.call(initData); - if (!success) revert InitializationFailed(); - } - - emit AccountCreated(_account, implementation, chainId, tokenContract, tokenId, salt); - - return _account; - } - - function account(address implementation, uint256 chainId, address tokenContract, uint256 tokenId, uint256 salt) - external - view - returns (address) - { - bytes32 bytecodeHash = keccak256(_creationCode(implementation, chainId, tokenContract, tokenId, salt)); - - return Create2.computeAddress(bytes32(salt), bytecodeHash); - } - - function _creationCode( - address implementation_, - uint256 chainId_, - address tokenContract_, - uint256 tokenId_, - uint256 salt_ - ) internal pure returns (bytes memory) { - return abi.encodePacked( - hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73", - implementation_, - hex"5af43d82803e903d91602b57fd5bf3", - abi.encode(salt_, chainId_, tokenContract_, tokenId_) - ); - } -} diff --git a/contracts/core/eip6551/interfaces/IERC6551Registry.sol b/contracts/core/eip6551/interfaces/IERC6551Registry.sol deleted file mode 100644 index de97b68..0000000 --- a/contracts/core/eip6551/interfaces/IERC6551Registry.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; - -interface IERC6551Registry { - /// @dev The registry SHALL emit the AccountCreated event upon successful account creation - event AccountCreated( - address account, address implementation, uint256 chainId, address tokenContract, uint256 tokenId, uint256 salt - ); - - /// @dev Creates a token bound account for an ERC-721 token. - /// - /// If account has already been created, returns the account address without calling create2. - /// - /// If initData is not empty and account has not yet been created, calls account with - /// provided initData after creation. - /// - /// Emits AccountCreated event. - /// - /// @return the address of the account - function createAccount( - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 salt, - bytes calldata initData - ) external returns (address); - - /// @dev Returns the computed address of a token bound account - /// - /// @return The computed address of the account - function account(address implementation, uint256 chainId, address tokenContract, uint256 tokenId, uint256 salt) - external - view - returns (address); -} From 6494760f5acbd0ecb594fa60aef92e6ff39d4d24 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 13 Nov 2023 10:34:47 +0100 Subject: [PATCH 04/83] Simplifying contracts and versions --- contracts/core/BaseOpenfortAccount.sol | 3 +- .../core/eip6551/EIP6551OpenfortAccount.sol | 3 +- .../eip6551/interfaces/IERC6551Account.sol | 2 +- contracts/core/eip6551/utils/Bytecode.sol | 2 +- .../core/managed/ManagedOpenfortAccount.sol | 3 +- .../core/managed/ManagedOpenfortFactory.sol | 3 +- .../core/managed/OpenfortBeaconProxy.sol | 3 +- .../recoverable/OpenfortRecoverableProxy.sol | 3 +- .../RecoverableOpenfortAccount.sol | 3 +- .../RecoverableOpenfortFactory.sol | 3 +- .../core/static/StaticOpenfortAccount.sol | 50 - .../core/static/StaticOpenfortFactory.sol | 64 - .../upgradeable/OpenfortUpgradeableProxy.sol | 3 +- .../UpgradeableOpenfortAccount.sol | 3 +- .../UpgradeableOpenfortFactory.sol | 3 +- contracts/interfaces/IBaseOpenfortFactory.sol | 2 +- .../interfaces/OpenfortErrorsAndEvents.sol | 2 +- .../mock/MockedV2ManagedOpenfortAccount.sol | 3 +- .../MockedV2UpgradeableOpenfortAccount.sol | 3 +- contracts/mock/Rewards.sol | 2 +- contracts/mock/SimpleNFT.sol | 5 +- contracts/mock/USDC.sol | 2 +- contracts/mock/VipNFT.sol | 2 +- contracts/paymaster/OpenfortPaymaster.sol | 198 --- contracts/paymaster/OpenfortPaymasterV2.sol | 1 - script/OpenfortForksConfig.s.sol | 2 +- script/UserOpTestCounter.sol | 2 +- script/checkDeposits.s.sol | 2 +- script/deployBatching.s.sol | 2 +- script/deployEIP6551.sol | 2 +- script/deployManagedAccounts.sol | 2 +- script/deployMock.sol | 2 +- script/deployOpenfortPaymaster.s.sol | 26 - script/deployOpenfortPaymasterV2.s.sol | 2 +- script/deployRecoverableAccounts.sol | 2 +- script/deployStaticAccounts.sol | 45 - script/deployUpgradeableAccounts.sol | 2 +- script/signMessage.sol | 2 +- test/foundry/4337/Specific4337Tests.t.sol | 2 +- .../eip6551/EIP6551OpenfortAccountTest.t.sol | 2 +- .../eip6551/EIP6551OpenfortBenchmark.t.sol | 2 +- .../managed/ManagedOpenfortAccountTest.t.sol | 2 +- .../RecoverableOpenfortAccountTest.t.sol | 2 +- .../static/StaticOpenfortAccountTest.t.sol | 1345 ----------------- .../UpgradeableOpenfortAccountTest.t.sol | 2 +- .../paymaster/OpenfortPaymasterTest.t.sol | 1313 ---------------- .../paymaster/OpenfortPaymasterV2Test.t.sol | 2 +- 47 files changed, 41 insertions(+), 3095 deletions(-) delete mode 100644 contracts/core/static/StaticOpenfortAccount.sol delete mode 100644 contracts/core/static/StaticOpenfortFactory.sol delete mode 100644 contracts/paymaster/OpenfortPaymaster.sol delete mode 100644 script/deployOpenfortPaymaster.s.sol delete mode 100644 script/deployStaticAccounts.sol delete mode 100644 test/foundry/core/static/StaticOpenfortAccountTest.t.sol delete mode 100644 test/foundry/paymaster/OpenfortPaymasterTest.t.sol diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index 4035d08..f8a9012 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; @@ -14,7 +14,6 @@ import "account-abstraction/core/Helpers.sol" as Helpers; /** * @title BaseOpenfortAccount (Non-upgradeable) - * @author Eloi * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. * It inherits from: * - BaseAccount to comply with ERC-4337 diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index df37cb1..512bcd4 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; @@ -16,7 +16,6 @@ import "account-abstraction/core/Helpers.sol" as Helpers; /** * @title EIP6551OpenfortAccount (Non-upgradeable) - * @author Eloi * @notice Smart contract wallet with session keys following the ERC-4337 and EIP-6551 standards. * It inherits from: * - BaseAccount to comply with ERC-4337 diff --git a/contracts/core/eip6551/interfaces/IERC6551Account.sol b/contracts/core/eip6551/interfaces/IERC6551Account.sol index d721063..ea2ac53 100644 --- a/contracts/core/eip6551/interfaces/IERC6551Account.sol +++ b/contracts/core/eip6551/interfaces/IERC6551Account.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; /// @dev the ERC-165 identifier for this interface is `0x400a0398` interface IERC6551Account { diff --git a/contracts/core/eip6551/utils/Bytecode.sol b/contracts/core/eip6551/utils/Bytecode.sol index 32ff62e..704ca5b 100644 --- a/contracts/core/eip6551/utils/Bytecode.sol +++ b/contracts/core/eip6551/utils/Bytecode.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; library Bytecode { error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end); diff --git a/contracts/core/managed/ManagedOpenfortAccount.sol b/contracts/core/managed/ManagedOpenfortAccount.sol index a3e3a95..eb29924 100644 --- a/contracts/core/managed/ManagedOpenfortAccount.sol +++ b/contracts/core/managed/ManagedOpenfortAccount.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; // Base account contract to inherit from and EntryPoint interface import {BaseOpenfortAccount, IEntryPoint} from "../BaseOpenfortAccount.sol"; /** * @title ManagedOpenfortAccount (Upgradeable via Beacon) - * @author Eloi * @notice Smart contract wallet managed via Beacon with session keys following the ERC-4337 standard. * It inherits from: * - BaseOpenfortAccount diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index 1520667..55f58f8 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; @@ -14,7 +14,6 @@ import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; /** * @title ManagedOpenfortFactory (Non-upgradeable) - * @author Eloi * @notice Contract to create an on-chain factory to deploy new ManagedOpenfortAccounts. * It uses OpenZeppelin's Create2 and OpenfortBeaconProxy libraries. * It inherits from: diff --git a/contracts/core/managed/OpenfortBeaconProxy.sol b/contracts/core/managed/OpenfortBeaconProxy.sol index 1cd0369..40d2095 100644 --- a/contracts/core/managed/OpenfortBeaconProxy.sol +++ b/contracts/core/managed/OpenfortBeaconProxy.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; /** * @title OpenfortBeaconProxy (Non-upgradeable) - * @author Eloi * @notice Contract to create the Beacon to determine implementation contract, which is where they will delegate all function calls. * It inherits from: * - BeaconProxy diff --git a/contracts/core/recoverable/OpenfortRecoverableProxy.sol b/contracts/core/recoverable/OpenfortRecoverableProxy.sol index 6b24e20..1982d83 100644 --- a/contracts/core/recoverable/OpenfortRecoverableProxy.sol +++ b/contracts/core/recoverable/OpenfortRecoverableProxy.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; /** * @title OpenfortRecoverableProxy - * @author Eloi * @notice Contract to create the proxies * It inherits from: * - ERC1967Proxy diff --git a/contracts/core/recoverable/RecoverableOpenfortAccount.sol b/contracts/core/recoverable/RecoverableOpenfortAccount.sol index 05a6c75..da2b92d 100644 --- a/contracts/core/recoverable/RecoverableOpenfortAccount.sol +++ b/contracts/core/recoverable/RecoverableOpenfortAccount.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; // Base account contract to inherit from and EntryPoint interface import {BaseOpenfortAccount, IEntryPoint, SafeCastUpgradeable, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; @@ -8,7 +8,6 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; /** * @title RecoverableOpenfortAccount - * @author Eloi * @notice Openfort account with session keys, guardians and pausability following the ERC-4337 standard. * It inherits from: * - BaseOpenfortAccount diff --git a/contracts/core/recoverable/RecoverableOpenfortFactory.sol b/contracts/core/recoverable/RecoverableOpenfortFactory.sol index a45ac31..bf8c100 100644 --- a/contracts/core/recoverable/RecoverableOpenfortFactory.sol +++ b/contracts/core/recoverable/RecoverableOpenfortFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; // Smart wallet implementation to use @@ -10,7 +10,6 @@ import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; /** * @title RecoverableOpenfortFactory (Non-upgradeable) - * @author Eloi * @notice Contract to create an on-chain factory to deploy new RecoverableOpenfortAccounts. * It uses OpenZeppelin's Create2 and OpenfortRecoverableProxy libraries. * It inherits from: diff --git a/contracts/core/static/StaticOpenfortAccount.sol b/contracts/core/static/StaticOpenfortAccount.sol deleted file mode 100644 index e122c3b..0000000 --- a/contracts/core/static/StaticOpenfortAccount.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; - -// Base account contract to inherit from and EntryPoint interface -import {BaseOpenfortAccount, IEntryPoint} from "../BaseOpenfortAccount.sol"; - -/** - * @title StaticOpenfortAccount (Non-upgradeable) - * @author Eloi - * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. - * The EntryPoint can be updated via updateEntryPoint(). - * It inherits from: - * - BaseOpenfortAccount - */ -contract StaticOpenfortAccount is BaseOpenfortAccount { - address internal entrypointContract; - - event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); - - /* - * @notice Initialize the smart contract wallet. - */ - function initialize(address _defaultAdmin, address _entrypoint) public initializer { - if (_defaultAdmin == address(0) || _entrypoint == address(0)) { - revert ZeroAddressNotAllowed(); - } - emit EntryPointUpdated(entrypointContract, _entrypoint); - _transferOwnership(_defaultAdmin); - entrypointContract = _entrypoint; - __EIP712_init("Openfort", "0.4"); - } - - /** - * Return the current EntryPoint - */ - function entryPoint() public view override returns (IEntryPoint) { - return IEntryPoint(entrypointContract); - } - - /** - * Update the EntryPoint address - */ - function updateEntryPoint(address _newEntrypoint) external onlyOwner { - if (_newEntrypoint == address(0)) { - revert ZeroAddressNotAllowed(); - } - emit EntryPointUpdated(entrypointContract, _newEntrypoint); - entrypointContract = _newEntrypoint; - } -} diff --git a/contracts/core/static/StaticOpenfortFactory.sol b/contracts/core/static/StaticOpenfortFactory.sol deleted file mode 100644 index 6c835c5..0000000 --- a/contracts/core/static/StaticOpenfortFactory.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; - -import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; -// Smart wallet implementation to use -import {StaticOpenfortAccount} from "./StaticOpenfortAccount.sol"; -// Interfaces -import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; - -/** - * @title StaticOpenfortFactory (Non-upgradeable) - * @author Eloi - * @notice Contract to create an on-chain factory to deploy new StaticOpenfortAccounts using OpenZeppelin's Clones library. - * As explained by OZ: The Clones library provides a way to deploy minimal non-upgradeable proxies for cheap. - * This can be useful for applications that require deploying many instances of the same contract (for example one per user, or one per task). - * These instances are designed to be both cheap to deploy, and cheap to call. - * The drawback being that they are not upgradeable. - * It inherits from: - * - IBaseOpenfortFactory - */ -contract StaticOpenfortFactory is IBaseOpenfortFactory { - address public immutable entrypointContract; - address public immutable accountImplementation; - - constructor(address _entrypoint, address _accountImplementation) { - if (_entrypoint == address(0) || _accountImplementation == address(0)) { - revert ZeroAddressNotAllowed(); - } - entrypointContract = _entrypoint; - accountImplementation = _accountImplementation; - } - - /* - * @notice Deploy a new account for _admin and a given nonce. - */ - function createAccountWithNonce(address _admin, bytes32 _nonce) external returns (address account) { - address impl = accountImplementation; - bytes32 salt = keccak256(abi.encode(_admin, _nonce)); - account = Clones.predictDeterministicAddress(impl, salt); - - if (account.code.length > 0) { - return account; - } - - emit AccountCreated(account, _admin); - account = Clones.cloneDeterministic(impl, salt); - _initializeAccount(account, _admin, entrypointContract); - } - - /* - * @notice Return the address of an account that would be deployed with the given admin signer and nonce. - */ - function getAddressWithNonce(address _admin, bytes32 _nonce) public view returns (address) { - bytes32 salt = keccak256(abi.encode(_admin, _nonce)); - return Clones.predictDeterministicAddress(accountImplementation, salt); - } - - /* - * @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. - */ - function _initializeAccount(address _account, address _admin, address _entrypointContract) internal { - StaticOpenfortAccount(payable(_account)).initialize(_admin, _entrypointContract); - } -} diff --git a/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol b/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol index cfd883c..63fc87a 100644 --- a/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol +++ b/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; /** * @title OpenfortUpgradeableProxy (Non-upgradeable) - * @author Eloi * @notice Contract to create the proxies * It inherits from: * - ERC1967Proxy diff --git a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol index a84d0e6..3ffb1d2 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; // Base account contract to inherit from and EntryPoint interface import {BaseOpenfortAccount, IEntryPoint} from "../BaseOpenfortAccount.sol"; @@ -7,7 +7,6 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U /** * @title UpgradeableOpenfortAccount - * @author Eloi * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. * It inherits from: * - BaseOpenfortAccount diff --git a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol index fd198ae..7e2411b 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; // Smart wallet implementation to use @@ -10,7 +10,6 @@ import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; /** * @title UpgradeableOpenfortFactory (Non-upgradeable) - * @author Eloi * @notice Contract to create an on-chain factory to deploy new UpgradeableOpenfortAccounts. * It uses OpenZeppelin's Create2 and OpenfortUpgradeableProxy libraries. * It inherits from: diff --git a/contracts/interfaces/IBaseOpenfortFactory.sol b/contracts/interfaces/IBaseOpenfortFactory.sol index ad90b8b..43949f3 100644 --- a/contracts/interfaces/IBaseOpenfortFactory.sol +++ b/contracts/interfaces/IBaseOpenfortFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; interface IBaseOpenfortFactory { /// @notice Emitted when a new Account is created. diff --git a/contracts/interfaces/OpenfortErrorsAndEvents.sol b/contracts/interfaces/OpenfortErrorsAndEvents.sol index 05b8627..4554381 100644 --- a/contracts/interfaces/OpenfortErrorsAndEvents.sol +++ b/contracts/interfaces/OpenfortErrorsAndEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; interface OpenfortErrorsAndEvents { /// @notice Error when a parameter is 0. diff --git a/contracts/mock/MockedV2ManagedOpenfortAccount.sol b/contracts/mock/MockedV2ManagedOpenfortAccount.sol index 4ddfa72..6e6689a 100644 --- a/contracts/mock/MockedV2ManagedOpenfortAccount.sol +++ b/contracts/mock/MockedV2ManagedOpenfortAccount.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; // Base account contract to inherit from import {BaseOpenfortAccount, IEntryPoint} from "../core/BaseOpenfortAccount.sol"; /** * @title ManagedOpenfortAccount (Upgradeable via Beacon) - * @author Eloi * @notice Smart contract wallet managed via Beacon with session keys following the ERC-4337 standard. * It inherits from: * - BaseOpenfortAccount diff --git a/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol b/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol index 4329b74..d32ba21 100644 --- a/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol +++ b/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; // Base account contract to inherit from import {BaseOpenfortAccount, IEntryPoint} from "../core/BaseOpenfortAccount.sol"; @@ -7,7 +7,6 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U /** * @title MockedV2UpgradeableOpenfortAccount - * @author Eloi * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. * It inherits from: * - BaseOpenfortAccount diff --git a/contracts/mock/Rewards.sol b/contracts/mock/Rewards.sol index 9cb6b67..d014a1b 100644 --- a/contracts/mock/Rewards.sol +++ b/contracts/mock/Rewards.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; diff --git a/contracts/mock/SimpleNFT.sol b/contracts/mock/SimpleNFT.sol index 581acc8..bcca742 100644 --- a/contracts/mock/SimpleNFT.sol +++ b/contracts/mock/SimpleNFT.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.15; +pragma solidity =0.8.19; /* solhint-disable reason-string */ +/* solhint-disable no-empty-blocks */ -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; // A simple ERC721 contract contract SimpleNFT is ERC721 { diff --git a/contracts/mock/USDC.sol b/contracts/mock/USDC.sol index 2a408bb..878e7a0 100644 --- a/contracts/mock/USDC.sol +++ b/contracts/mock/USDC.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; diff --git a/contracts/mock/VipNFT.sol b/contracts/mock/VipNFT.sol index b7450ba..503b92e 100644 --- a/contracts/mock/VipNFT.sol +++ b/contracts/mock/VipNFT.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; diff --git a/contracts/paymaster/OpenfortPaymaster.sol b/contracts/paymaster/OpenfortPaymaster.sol deleted file mode 100644 index dadba4b..0000000 --- a/contracts/paymaster/OpenfortPaymaster.sol +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.19; - -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; -import {UserOperation, UserOperationLib, IEntryPoint} from "account-abstraction/core/BaseAccount.sol"; -import "account-abstraction/core/Helpers.sol" as Helpers; -import {BaseOpenfortPaymaster} from "./BaseOpenfortPaymaster.sol"; -import {OpenfortErrorsAndEvents} from "../interfaces/OpenfortErrorsAndEvents.sol"; - -/** - * @title OpenfortPaymaster (Non-upgradeable) - * @author Eloi - * @notice A paymaster that uses external service to decide whether to pay for the UserOp. - * The Paymaster trusts an external signer (owner) to sign each user operation. - * The calling user must pass the UserOp to that external signer first, which performs - * whatever off-chain verification before signing the UserOp. - * It has the following features: - * - Sponsor the whole UserOp (PayForUser mode) - * - Let the sender pay fees in ERC20 (both using an exchange rate per gas or per userOp) - * - All ERC20s used to sponsor gas go to the address `tokenRecipient` - */ -contract OpenfortPaymaster is BaseOpenfortPaymaster { - using ECDSA for bytes32; - using UserOperationLib for UserOperation; - using SafeERC20 for IERC20; - - uint256 private constant SIGNATURE_OFFSET = 180; // 20+48+48+32+32 = 180 - - address public tokenRecipient; - - enum Mode { - PayForUser, - DynamicRate, - FixedRate - } - - struct PolicyStrategy { - Mode paymasterMode; - address erc20Token; - uint256 exchangeRate; - } - - /// @notice When a transaction has been paid using an ERC20 token - event GasPaidInERC20(address erc20Token, uint256 actualGasCost, uint256 actualTokensSent); - - /// @notice When the owner deposits gas to the EntryPoint - event GasDeposited(address indexed from, address indexed depositor, uint256 indexed value); - - /// @notice When the owner withdraws gas from the EntryPoint - event GasWithdrawn(address indexed depositor, address indexed to, uint256 indexed value); - - /// @notice When tokenRecipient changes - event TokenRecipientUpdated(address oldTokenRecipient, address newTokenRecipient); - - event PostOpReverted(bytes context, uint256 actualGasCost); - - constructor(IEntryPoint _entryPoint, address _owner) BaseOpenfortPaymaster(_entryPoint, _owner) { - tokenRecipient = _owner; - } - - /** - * Return the hash we're going to sign off-chain (and validate on-chain) - * this method is called by the off-chain service, to sign the request. - * it is called on-chain from the validatePaymasterUserOp, to validate the signature. - * note that this signature covers all fields of the UserOperation, except the "paymasterAndData", - * which will carry the signature itself. - */ - function getHash( - UserOperation calldata userOp, - uint48 validUntil, - uint48 validAfter, - PolicyStrategy memory strategy - ) public view returns (bytes32) { - // Dividing the hashing in 2 parts due to the stack too deep error - bytes memory firstHalf = abi.encode( - userOp.getSender(), - userOp.nonce, - keccak256(userOp.initCode), - keccak256(userOp.callData), - userOp.callGasLimit, - userOp.verificationGasLimit, - userOp.preVerificationGas, - userOp.maxFeePerGas, - userOp.maxPriorityFeePerGas - ); - bytes memory secondHalf = abi.encode(block.chainid, address(this), validUntil, validAfter, strategy); - return keccak256(abi.encodePacked(firstHalf, secondHalf)); - } - - /** - * Verify that the paymaster owner has signed this request. - * The "paymasterAndData" is expected to be the paymaster and a signature over the entire request params - * paymasterAndData[:ADDRESS_OFFSET]: address(this) - * paymasterAndData[ADDRESS_OFFSET:SIGNATURE_OFFSET]: abi.encode(validUntil, validAfter, strategy) // 20+48+48+32+32+32 - * paymasterAndData[SIGNATURE_OFFSET:]: signature - */ - function _validatePaymasterUserOp( - UserOperation calldata userOp, - bytes32, /*userOpHash*/ - uint256 /*requiredPreFund*/ - ) internal view override returns (bytes memory context, uint256 validationData) { - (uint48 validUntil, uint48 validAfter, PolicyStrategy memory strategy, bytes calldata signature) = - parsePaymasterAndData(userOp.paymasterAndData); - - bytes32 hash = ECDSA.toEthSignedMessageHash(getHash(userOp, validUntil, validAfter, strategy)); - - // Don't revert on signature failure: return SIG_VALIDATION_FAILED with empty context - if (owner() != ECDSA.recover(hash, signature)) { - return ("", Helpers._packValidationData(true, validUntil, validAfter)); - } - - context = abi.encode(userOp.sender, strategy, userOp.maxFeePerGas, userOp.maxPriorityFeePerGas); - - // If the parsePaymasterAndData was signed by the owner of the paymaster - // return the context and validity (validUntil, validAfter). - return (context, Helpers._packValidationData(false, validUntil, validAfter)); - } - - /* - * For ERC20 modes (DynamicRate and FixedRate), transfer the right amount of tokens from the sender to the designated recipient - */ - function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal override { - if (mode == PostOpMode.postOpReverted) { - emit PostOpReverted(context, actualGasCost); - // Do nothing here to not revert the whole bundle and harm reputation - From ethInfinitism - return; - } - (address sender, PolicyStrategy memory strategy, uint256 maxFeePerGas, uint256 maxPriorityFeePerGas) = - abi.decode(context, (address, PolicyStrategy, uint256, uint256)); - - // Getting OP gas price - uint256 opGasPrice; - unchecked { - if (maxFeePerGas == maxPriorityFeePerGas) { - // Legacy mode (for networks that do not support basefee opcode) - opGasPrice = maxFeePerGas; - } else { - opGasPrice = Math.min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); - } - } - - uint256 actualOpCost = actualGasCost + (postOpGas * opGasPrice); - - if (strategy.paymasterMode == Mode.DynamicRate) { - uint256 actualTokenCost = actualOpCost * strategy.exchangeRate; - emit GasPaidInERC20(address(strategy.erc20Token), actualOpCost, actualTokenCost); - IERC20(strategy.erc20Token).safeTransferFrom(sender, tokenRecipient, actualTokenCost); - } else if (strategy.paymasterMode == Mode.FixedRate) { - emit GasPaidInERC20(address(strategy.erc20Token), actualOpCost, strategy.exchangeRate); - IERC20(strategy.erc20Token).safeTransferFrom(sender, tokenRecipient, strategy.exchangeRate); - } - } - - /** - * Parse paymasterAndData - * The "paymasterAndData" is expected to be the paymaster and a signature over the entire request params - * paymasterAndData[:ADDRESS_OFFSET]: address(this) - * paymasterAndData[ADDRESS_OFFSET:SIGNATURE_OFFSET]: (validUntil, validAfter, strategy) - * paymasterAndData[SIGNATURE_OFFSET:]: signature - */ - function parsePaymasterAndData(bytes calldata paymasterAndData) - public - pure - returns (uint48 validUntil, uint48 validAfter, PolicyStrategy memory strategy, bytes calldata signature) - { - (validUntil, validAfter, strategy) = - abi.decode(paymasterAndData[ADDRESS_OFFSET:SIGNATURE_OFFSET], (uint48, uint48, PolicyStrategy)); - signature = paymasterAndData[SIGNATURE_OFFSET:]; - } - - /** - * @dev Override the default implementation. - */ - function deposit() public payable override { - entryPoint.depositTo{value: msg.value}(address(this)); - emit GasDeposited(msg.sender, msg.sender, msg.value); - } - - /** - * @inheritdoc BaseOpenfortPaymaster - */ - function withdrawTo(address payable _withdrawAddress, uint256 _amount) public override onlyOwner { - entryPoint.withdrawTo(_withdrawAddress, _amount); - emit GasWithdrawn(msg.sender, _withdrawAddress, _amount); - } - - /** - * Allows the owner of the paymaster to update the token recipient address - */ - function updateTokenRecipient(address _newTokenRecipient) external onlyOwner { - if (_newTokenRecipient == address(0)) revert OpenfortErrorsAndEvents.ZeroValueNotAllowed(); - emit TokenRecipientUpdated(tokenRecipient, _newTokenRecipient); - tokenRecipient = _newTokenRecipient; - } -} diff --git a/contracts/paymaster/OpenfortPaymasterV2.sol b/contracts/paymaster/OpenfortPaymasterV2.sol index 2b95b3b..73829d1 100644 --- a/contracts/paymaster/OpenfortPaymasterV2.sol +++ b/contracts/paymaster/OpenfortPaymasterV2.sol @@ -12,7 +12,6 @@ import {OpenfortErrorsAndEvents} from "../interfaces/OpenfortErrorsAndEvents.sol /** * @title OpenfortPaymasterV2 (Non-upgradeable) - * @author Eloi * @notice A paymaster that uses external service to decide whether to pay for the UserOp. * The Paymaster trusts an external signer (owner) to sign each user operation. * The calling user must pass the UserOp to that external signer first, which performs diff --git a/script/OpenfortForksConfig.s.sol b/script/OpenfortForksConfig.s.sol index 2000cdb..3df259c 100644 --- a/script/OpenfortForksConfig.s.sol +++ b/script/OpenfortForksConfig.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script} from "forge-std/Script.sol"; diff --git a/script/UserOpTestCounter.sol b/script/UserOpTestCounter.sol index 53d48c4..5dcedc5 100644 --- a/script/UserOpTestCounter.sol +++ b/script/UserOpTestCounter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/script/checkDeposits.s.sol b/script/checkDeposits.s.sol index 7de8578..5a62cb1 100644 --- a/script/checkDeposits.s.sol +++ b/script/checkDeposits.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {console} from "forge-std/console.sol"; import {IBaseOpenfortPaymaster} from "../contracts/interfaces/IBaseOpenfortPaymaster.sol"; diff --git a/script/deployBatching.s.sol b/script/deployBatching.s.sol index e33137b..8cac5de 100644 --- a/script/deployBatching.s.sol +++ b/script/deployBatching.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/script/deployEIP6551.sol b/script/deployEIP6551.sol index 52b1d3b..c30def5 100644 --- a/script/deployEIP6551.sol +++ b/script/deployEIP6551.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; diff --git a/script/deployManagedAccounts.sol b/script/deployManagedAccounts.sol index 3470c09..813ed2b 100644 --- a/script/deployManagedAccounts.sol +++ b/script/deployManagedAccounts.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; diff --git a/script/deployMock.sol b/script/deployMock.sol index 6592579..f0866a6 100644 --- a/script/deployMock.sol +++ b/script/deployMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; diff --git a/script/deployOpenfortPaymaster.s.sol b/script/deployOpenfortPaymaster.s.sol deleted file mode 100644 index d379898..0000000 --- a/script/deployOpenfortPaymaster.s.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; - -import {Script} from "forge-std/Script.sol"; -import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; -import {OpenfortPaymaster} from "../contracts/paymaster/OpenfortPaymaster.sol"; - -contract OpenfortPaymasterDeploy is Script { - uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC_PAYMASTER_OWNER_TESTNET"), 0); - // uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); - address internal deployAddress = vm.addr(deployPrivKey); - IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - uint32 internal constant UNSTAKEDELAYSEC = 8600; - - function run() public { - bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); - vm.startBroadcast(deployPrivKey); - - OpenfortPaymaster openfortPaymaster = new OpenfortPaymaster{salt: versionSalt}(entryPoint, deployAddress); - - entryPoint.depositTo{value: 0.5 ether}(address(openfortPaymaster)); - openfortPaymaster.addStake{value: 0.25 ether}(UNSTAKEDELAYSEC); - - vm.stopBroadcast(); - } -} diff --git a/script/deployOpenfortPaymasterV2.s.sol b/script/deployOpenfortPaymasterV2.s.sol index 9bed182..e30389e 100644 --- a/script/deployOpenfortPaymasterV2.s.sol +++ b/script/deployOpenfortPaymasterV2.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; diff --git a/script/deployRecoverableAccounts.sol b/script/deployRecoverableAccounts.sol index 4d6dee1..eef51e8 100644 --- a/script/deployRecoverableAccounts.sol +++ b/script/deployRecoverableAccounts.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; diff --git a/script/deployStaticAccounts.sol b/script/deployStaticAccounts.sol deleted file mode 100644 index b4016cd..0000000 --- a/script/deployStaticAccounts.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; - -import {Script, console} from "forge-std/Script.sol"; -import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; -import {StaticOpenfortAccount} from "../contracts/core/static/StaticOpenfortAccount.sol"; -import {StaticOpenfortFactory} from "../contracts/core/static/StaticOpenfortFactory.sol"; - -contract StaticOpenfortDeploy is Script { - uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); - address internal deployAddress = vm.addr(deployPrivKey); - IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - - function run() public { - bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); - vm.startBroadcast(deployPrivKey); - - // Create an acccount to server as implementation - StaticOpenfortAccount staticOpenfortAccount = new StaticOpenfortAccount{salt: versionSalt}(); - - // Create a factory to deploy cloned accounts - StaticOpenfortFactory staticOpenfortFactory = - new StaticOpenfortFactory{salt: versionSalt}(address(entryPoint), address(staticOpenfortAccount)); - // address account1 = staticOpenfortFactory.accountImplementation(); - - // The first call should create a new account, while the second will just return the corresponding account address - address account2 = staticOpenfortFactory.createAccountWithNonce(deployAddress, "1"); - console.log( - "Factory at address %s has created an account at address %s", address(staticOpenfortFactory), account2 - ); - // assert(account1 != account2); - // address account3 = staticOpenfortFactory.createAccountWithNonce(deployAddress, 3); - // console.log( - // "Factory at address %s has created an account at address %s", address(staticOpenfortFactory), account3 - // ); - // assert(account2 != account3); - // address account4 = staticOpenfortFactory.createAccountWithNonce(deployAddress, 4); - // console.log( - // "Factory at address %s has created an account at address %s", address(staticOpenfortFactory), account4 - // ); - // assert(account3 != account4); - - vm.stopBroadcast(); - } -} diff --git a/script/deployUpgradeableAccounts.sol b/script/deployUpgradeableAccounts.sol index bc9c712..88c9e08 100644 --- a/script/deployUpgradeableAccounts.sol +++ b/script/deployUpgradeableAccounts.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; diff --git a/script/signMessage.sol b/script/signMessage.sol index bdd5f3c..2dd26d1 100644 --- a/script/signMessage.sol +++ b/script/signMessage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; diff --git a/test/foundry/4337/Specific4337Tests.t.sol b/test/foundry/4337/Specific4337Tests.t.sol index bb1cf5f..705ffec 100644 --- a/test/foundry/4337/Specific4337Tests.t.sol +++ b/test/foundry/4337/Specific4337Tests.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol index 72666f6..0298683 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {SigUtils} from "../../utils/SigUtils.sol"; diff --git a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol index c1c3196..77d1936 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {SigUtils} from "../../utils/SigUtils.sol"; diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 2168d89..f25b6d1 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol index 52e0e87..600ce93 100644 --- a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol +++ b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/test/foundry/core/static/StaticOpenfortAccountTest.t.sol b/test/foundry/core/static/StaticOpenfortAccountTest.t.sol deleted file mode 100644 index 5876e0d..0000000 --- a/test/foundry/core/static/StaticOpenfortAccountTest.t.sol +++ /dev/null @@ -1,1345 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; - -import {Test, console} from "lib/forge-std/src/Test.sol"; -import {SigUtils} from "../../utils/SigUtils.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {TestToken} from "account-abstraction/test/TestToken.sol"; -import {StaticOpenfortAccount} from "contracts/core/static/StaticOpenfortAccount.sol"; -import {StaticOpenfortFactory} from "contracts/core/static/StaticOpenfortFactory.sol"; - -contract StaticOpenfortAccountTest is Test { - using ECDSA for bytes32; - - EntryPoint public entryPoint; - StaticOpenfortAccount public staticOpenfortAccount; - StaticOpenfortFactory public staticOpenfortFactory; - address public account; - TestCounter public testCounter; - TestToken public testToken; - - // Testing addresses - address private factoryAdmin; - uint256 private factoryAdminPKey; - - address private accountAdmin; - uint256 private accountAdminPKey; - - address payable private beneficiary = payable(makeAddr("beneficiary")); - - event AccountCreated(address indexed account, address indexed accountAdmin); - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (UserOperation[] memory ops) { - uint256 nonce = entryPoint.getNonce(sender, 0); - - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 0, - maxPriorityFeePerGas: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /** - * @notice Initialize the StaticOpenfortAccount testing contract. - * Scenario: - * - factoryAdmin is the deployer (and owner) of the StaticOpenfortFactory - * - accountAdmin is the account used to deploy new static accounts - * - entryPoint is the singleton EntryPoint - * - testCounter is the counter used to test userOps - */ - function setUp() public { - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - - vm.startPrank(factoryAdmin); - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } - // deploy static account implementation - staticOpenfortAccount = new StaticOpenfortAccount(); - // deploy static account factory - staticOpenfortFactory = new StaticOpenfortFactory(payable(address(entryPoint)), address(staticOpenfortAccount)); - // Create an static account wallet and get its address - account = staticOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); - // deploy a new TestCounter - testCounter = new TestCounter(); - // deploy a new TestToken (ERC20) - testToken = new TestToken(); - vm.stopPrank(); - } - - /* - * Create an account by directly calling the factory. - */ - function testCreateAccountWithNonceViaFactory() public { - // Get the counterfactual address - vm.prank(factoryAdmin); - address accountAddress2 = staticOpenfortFactory.getAddressWithNonce(accountAdmin, "2"); - - // Expect that we will see an event containing the account and admin - vm.expectEmit(true, true, false, true); - emit AccountCreated(accountAddress2, accountAdmin); - - // Deploy a static account to the counterfactual address - vm.prank(factoryAdmin); - staticOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); - - // Calling it again should just return the address and not create another account - vm.prank(factoryAdmin); - staticOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); - - // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); - assertEq(accountAddress2, staticOpenfortFactory.getAddressWithNonce(accountAdmin, "2")); - } - - /* - * Create an account calling the factory via EntryPoint. - * Use initCode - */ - function testCreateAccountViaEntryPoint() public { - // It is not correct to use the Factory using the EntryPoint anymore - // Accounts created using factories are depend on msg.sender now - // revert(); - - // Make sure the smart account does not have any code yet - address account2 = staticOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); - assertEq(account2.code.length, 0); - - bytes memory initCallData = - abi.encodeWithSignature("createAccountWithNonce(address,bytes32)", accountAdmin, bytes32("2")); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(staticOpenfortFactory)), initCallData); - - UserOperation[] memory userOpCreateAccount = - _setupUserOpExecute(account2, accountAdminPKey, initCode, address(0), 0, bytes("")); - - // Expect that we will see an event containing the account and admin - vm.expectEmit(true, true, false, true); - emit AccountCreated(account2, accountAdmin); - - entryPoint.handleOps(userOpCreateAccount, beneficiary); - - // Make sure the smart account does have some code now - assert(account2.code.length > 0); - - // Make sure the counterfactual address has not been altered - assertEq(account2, staticOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); - } - - /* - * Create an account using the factory and make it call count() directly. - */ - function testIncrementCounterDirect() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - // Make the admin of the static account wallet (deployer) call "count" - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).execute(address(testCounter), 0, abi.encodeWithSignature("count()")); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Create an account by directly calling the factory and make it call count() - * using the execute() function using the EntryPoint (userOp). Leaveraging ERC-4337. - */ - function testIncrementCounterViaEntrypoint() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Create an account by directly calling the factory and make it call count() - * using the executeBatching() function using the EntryPoint (userOp). Leaveraging ERC-4337. - */ - function testIncrementCounterViaEntrypointBatching() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 3); - } - - /* - * Should fail, try to use a sessionKey that is not registered. - */ - function testFailIncrementCounterViaSessionKeyNotregistered() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Use a sessionKey that is registered. - */ - function testIncrementCounterViaSessionKey() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Register a sessionKey via userOp calling the execute() function - * using the EntryPoint (userOp). Then use the sessionKey to count - */ - function testRegisterSessionKeyViaEntrypoint() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - account, - 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - - userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Register a master sessionKey via userOp calling the execute() function - * using the EntryPoint (userOp). Then use that sessionKey to register a second one - */ - function testRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - account, - 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - - userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - - address sessionKeyAttack; - uint256 sessionKeyPrivKeyAttack; - (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); - - userOp = _setupUserOpExecute( - account, - sessionKeyPrivKey, - bytes(""), - account, - 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKeyAttack, 0, 2 ** 48 - 1) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - } - - /* - * Register a limited sessionKey via userOp calling the execute() function - * using the EntryPoint (userOp). Then use that sessionKey to register a second one - */ - function testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - account, - 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1, 10) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - - userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - - // Verify that the registered key is not a MasterKey nor has whitelisting - bool isMasterKey; - bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = StaticOpenfortAccount(payable(account)).sessionKeys(sessionKey); - assert(!isMasterKey); - assert(!isWhitelisted); - - address sessionKeyAttack; - uint256 sessionKeyPrivKeyAttack; - (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); - - userOp = _setupUserOpExecute( - account, - sessionKeyPrivKey, - bytes(""), - account, - 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKeyAttack, 0, 2 ** 48 - 1) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - } - - /* - * Should fail, try to use a sessionKey that is expired. - */ - function testIncrementCounterViaSessionKeyExpired() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - vm.warp(100); - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - vm.expectRevert(); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Should fail, try to use a sessionKey that is revoked. - */ - function testFailIncrementCounterViaSessionKeyRevoked() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0); - StaticOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Should fail, try to use a sessionKey that reached its limit. - */ - function testFailIncrementCounterViaSessionKeyReachLimit() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - // We are now in block 100, but our session key is valid until block 150 - vm.warp(100); - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has only increased by one - assertEq(testCounter.counters(account), 1); - } - - /* - * Should fail, try to use a sessionKey that reached its limit. - */ - function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - // We are now in block 100, but our session key is valid until block 150 - vm.warp(100); - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Should fail, try to revoke a sessionKey using a non-privileged user - */ - function testFailRevokeSessionKeyInvalidUser() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0); - vm.prank(beneficiary); - StaticOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Use a sessionKey with whitelisting to call Execute(). - */ - function testIncrementCounterViaSessionKeyWhitelisting() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](1); - whitelist[0] = address(testCounter); - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Should fail, try to register a sessionKey with a large whitelist. - */ - function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](11); - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Use a sessionKey with whitelisting to call ExecuteBatch(). - */ - function testIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](1); - whitelist[0] = address(testCounter); - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); - - // Verify that the registered key is not a MasterKey but has whitelisting - bool isMasterKey; - bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = StaticOpenfortAccount(payable(account)).sessionKeys(sessionKey); - assert(!isMasterKey); - assert(isWhitelisted); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 3); - } - - /* - * Use a sessionKey with whitelisting to call ExecuteBatch(). - */ - function testFailIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](1); - whitelist[0] = address(testCounter); - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); - - // Verify that the registered key is not a MasterKey but has whitelisting - bool isMasterKey; - bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = StaticOpenfortAccount(payable(account)).sessionKeys(sessionKey); - assert(!isMasterKey); - assert(isWhitelisted); - - uint256 count = 11; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). - */ - function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](1); - whitelist[0] = address(account); - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Should fail, try to use a sessionKey with invalid whitelisting to call ExecuteBatch(). - */ - function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](1); - whitelist[0] = address(account); - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = _setupUserOpExecuteBatch( - account, - sessionKeyPrivKey, //Sign the userOp using the sessionKey's private key - bytes(""), - targets, - values, - callData - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Change the owner of an account and call TestCounter directly. - * Important use-case: - * 1- accountAdmin is Openfort's master wallet and is managing the account of the user. - * 2- The user claims the ownership of the account to Openfort so Openfort calls - * transferOwnership() to the account. - * 3- The user has to "officially" claim the ownership of the account by directly - * interacting with the smart contract using the acceptOwnership() function. - * 4- From now on, the user is the owner of the account and can register and revoke session keys themselves. - * 5- Test that the new owner can directly interact with the account and make it call the testCounter contract. - */ - function testChangeOwnershipAndCountDirect() public { - address accountAdmin2; - uint256 accountAdmin2PKey; - (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); - - vm.expectRevert("Ownable: caller is not the owner"); - StaticOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); - - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); - vm.prank(accountAdmin2); - StaticOpenfortAccount(payable(account)).acceptOwnership(); - - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - // Make the admin of the static account wallet (deployer) call "count" - vm.prank(accountAdmin2); - StaticOpenfortAccount(payable(account)).execute(address(testCounter), 0, abi.encodeWithSignature("count()")); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Change the owner of an account and call TestCounter though the Entrypoint - */ - function testChangeOwnershipAndCountEntryPoint() public { - address accountAdmin2; - uint256 accountAdmin2PKey; - (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); - - vm.prank(accountAdmin); - StaticOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); - vm.prank(accountAdmin2); - StaticOpenfortAccount(payable(account)).acceptOwnership(); - - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Test an account with testToken instead of TestCount. - */ - function testMintTokenAccount() public { - // Verifiy that the totalSupply is stil 0 - assertEq(testToken.totalSupply(), 0); - - // Mint 1 to beneficiary - testToken.mint(beneficiary, 1); - assertEq(testToken.totalSupply(), 1); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verifiy that the totalSupply has increased - assertEq(testToken.totalSupply(), 2); - } - - /* - * Test receive native tokens. - */ - function testReceiveNativeToken() public { - assertEq(address(account).balance, 0); - - vm.prank(accountAdmin); - (bool success,) = payable(account).call{value: 1000}(""); - assert(success); - assertEq(address(account).balance, 1000); - } - - /* - * Transfer native tokens out of an account. - */ - function testTransferOutNativeToken() public { - uint256 value = 1000; - - assertEq(address(account).balance, 0); - vm.prank(accountAdmin); - (bool success,) = payable(account).call{value: value}(""); - assertEq(address(account).balance, value); - assert(success); - assertEq(beneficiary.balance, 0); - - UserOperation[] memory userOp = - _setupUserOpExecute(account, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); - - EntryPoint(entryPoint).handleOps(userOp, beneficiary); - assertEq(beneficiary.balance, value); - } - - /* - * Basic test of simulateValidation() to check that it always reverts. - */ - function testSimulateValidation() public { - // Verifiy that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - // Expect the simulateValidation() to always revert - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * 1- Deploy a factory using the old EntryPoint to create an account. - * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() - */ - function testUpdateEntryPoint() public { - address oldEntryPoint = address(0x0576a174D229E3cFA37253523E645A78A0C91B57); - address newEntryPoint = vm.envAddress("ENTRY_POINT_ADDRESS"); - StaticOpenfortFactory staticOpenfortFactoryOld = - new StaticOpenfortFactory(payable(oldEntryPoint), address(staticOpenfortAccount)); - - // Create an static account wallet using the old EntryPoint and get its address - address payable accountOld = payable(staticOpenfortFactoryOld.createAccountWithNonce(accountAdmin, "99")); - StaticOpenfortAccount staticAccount = StaticOpenfortAccount(accountOld); - assertEq(address(staticAccount.entryPoint()), oldEntryPoint); - - vm.expectRevert("Ownable: caller is not the owner"); - staticAccount.updateEntryPoint(newEntryPoint); - - vm.prank(accountAdmin); - staticAccount.updateEntryPoint(newEntryPoint); - - assertEq(address(staticAccount.entryPoint()), newEntryPoint); - } - - /* - * - * - */ - function testRegularSignature() public { - StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account)); - - // First regular hashing and signing using the EOA - bytes memory text = "Signed by Openfort"; - bytes32 hash = keccak256(text); - console.logBytes32(hash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - console.logBytes(signature); - bytes4 validSig = openfortAccount.isValidSignature(hash, signature); - console.logBytes4(validSig); - assert(validSig == 0x1626ba7e); // Should [PASS]. Regular signatures should work - - address signer = ecrecover(hash, v, r, s); - assertEq(openfortAccount.owner(), signer); // Should [PASS] - assertEq(accountAdmin, signer); // Should [PASS] - } - - /* - * - * - */ - function testEIP191Signature() public { - StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account)); - - // Now using EIP-191. isValidSignature() should return the MAGIC VALUE too - bytes memory text = "Signed by Openfort"; - bytes32 hash = keccak256(text); - bytes32 hash191 = hash.toEthSignedMessageHash(); - console.logBytes32(hash191); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hash191); - bytes memory signature191 = abi.encodePacked(r, s, v); - console.logBytes(signature191); - bytes4 validSig = openfortAccount.isValidSignature(hash, signature191); - console.logBytes4(validSig); - assert(validSig == 0x1626ba7e); // Should [PASS]. EIP-191 signatures should work too - - address signer = ecrecover(hash191, v, r, s); - assertEq(openfortAccount.owner(), signer); // Should [PASS] - assertEq(accountAdmin, signer); // Should [PASS] - } - - /* - * Test EIP712 Typed Signatures using isValidSignature's (EIP-1271) - */ - function testEIP712Signature() public { - StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account)); - - // Now using EIP-712. isValidSignature() should return the MAGIC VALUE too - bytes32 hash = keccak256("Signed by Openfort"); - (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = - openfortAccount.eip712Domain(); - console.log(name); - bytes32 _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - bytes32 domainSeparator = keccak256( - abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) - ); - bytes32 hash712 = domainSeparator.toTypedDataHash(hash); - - console.logBytes32(hash712); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hash712); - bytes memory signature721 = abi.encodePacked(r, s, v); - - console.logBytes(signature721); - { - bytes4 validSig = openfortAccount.isValidSignature(hash, signature721); - console.logBytes4(validSig); - assert(validSig == 0x1626ba7e); // Should [PASS]. EIP-712 typed signatures should work too - } - address signer = ecrecover(hash712, v, r, s); - assertEq(openfortAccount.owner(), signer); // Should [PASS] - assertEq(accountAdmin, signer); // Should [PASS] - } - - /* - * Test FAIL EIP712 Typed Signatures using isValidSignature's (EIP-1271) - * Altered the ChainID - */ - function testFailEIP712Signature() public { - StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account)); - - // Now using EIP-712. isValidSignature() should return the MAGIC VALUE too - bytes32 hash = keccak256("Signed by Openfort"); - (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = - openfortAccount.eip712Domain(); - console.log(name); - chainId = 9999; - bytes32 _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - bytes32 domainSeparator = keccak256( - abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) - ); - bytes32 hash712 = domainSeparator.toTypedDataHash(hash); - - console.logBytes32(hash712); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hash712); - bytes memory signature721 = abi.encodePacked(r, s, v); - - console.logBytes(signature721); - { - bytes4 validSig = openfortAccount.isValidSignature(hash, signature721); - console.logBytes4(validSig); - assert(validSig == 0x1626ba7e); // Should [PASS]. EIP-712 typed signatures should work too - } - address signer = ecrecover(hash712, v, r, s); - assertEq(openfortAccount.owner(), signer); // Should [PASS] - assertEq(accountAdmin, signer); // Should [PASS] - } - - /* - * Test Session key Signatures using isValidSignature's (EIP-1271) - */ - function testSessionKeySignature() public { - StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account)); - - // Now using Session keys. isValidSignature() should return the MAGIC VALUE too - bytes32 hash = keccak256("Signed by Openfort"); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - vm.warp(100); - vm.prank(accountAdmin); - openfortAccount.registerSessionKey(sessionKey, 0, 150); - bytes32 domainSeparator; - { - (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = - openfortAccount.eip712Domain(); - console.log(name); - bytes32 _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - domainSeparator = keccak256( - abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) - ); - } - bytes32 hash712 = domainSeparator.toTypedDataHash(hash); - console.logBytes32(hash712); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(sessionKeyPrivKey, hash712); - bytes memory signature721 = abi.encodePacked(r, s, v); - console.logBytes(signature721); - bytes4 validSig = openfortAccount.isValidSignature(hash, signature721); - console.logBytes4(validSig); - assert(validSig == 0x1626ba7e); // Should [PASS]. EIP-712 typed signatures should work too - address signer = ecrecover(hash712, v, r, s); - assertEq(sessionKey, signer); // Should [PASS] - assertNotEq(accountAdmin, signer); // Should [PASS] - } - - /* - * Test FAIL Session key Signatures using isValidSignature's (EIP-1271) - * Session key has expired - */ - function testFailSessionKeySignatureExpired() public { - StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account)); - - // Now using Session keys. isValidSignature() should return the MAGIC VALUE too - bytes32 hash = keccak256("Signed by Openfort"); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - // We are now at timestamp 100 - vm.warp(100); - vm.prank(accountAdmin); - openfortAccount.registerSessionKey(sessionKey, 0, 99); // register an expired session key - bytes32 domainSeparator; - { - (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = - openfortAccount.eip712Domain(); - console.log(name); - bytes32 _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - domainSeparator = keccak256( - abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) - ); - } - bytes32 hash712 = domainSeparator.toTypedDataHash(hash); - console.logBytes32(hash712); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(sessionKeyPrivKey, hash712); - bytes memory signature721 = abi.encodePacked(r, s, v); - console.logBytes(signature721); - bytes4 validSig = openfortAccount.isValidSignature(hash, signature721); - console.logBytes4(validSig); - assert(validSig == 0x1626ba7e); // Should fail because the session key is expired - address signer = ecrecover(hash712, v, r, s); - assertEq(sessionKey, signer); // Should [PASS] - assertNotEq(accountAdmin, signer); // Should [PASS] - } - - /* - * Test FAIL Session key Signatures using isValidSignature's (EIP-1271) - * Session key is not valid yet - */ - function testFailSessionKeySignatureNotYet() public { - StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account)); - - // Now using Session keys. isValidSignature() should return the MAGIC VALUE too - bytes32 hash = keccak256("Signed by Openfort"); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - // We are now at timestamp 100 - vm.warp(100); - vm.prank(accountAdmin); - openfortAccount.registerSessionKey(sessionKey, 150, 250); // register a session key that is not valid yet - bytes32 domainSeparator; - { - (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = - openfortAccount.eip712Domain(); - console.log(name); - bytes32 _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - domainSeparator = keccak256( - abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) - ); - } - bytes32 hash712 = domainSeparator.toTypedDataHash(hash); - console.logBytes32(hash712); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(sessionKeyPrivKey, hash712); - bytes memory signature721 = abi.encodePacked(r, s, v); - console.logBytes(signature721); - bytes4 validSig = openfortAccount.isValidSignature(hash, signature721); - console.logBytes4(validSig); - assert(validSig == 0x1626ba7e); // Should fail because the session key is expired - address signer = ecrecover(hash712, v, r, s); - assertEq(sessionKey, signer); // Should [PASS] - assertNotEq(accountAdmin, signer); // Should [PASS] - } - - /* - * Test FAIL Session key Signatures using isValidSignature's (EIP-1271) - * Session key is not a valid one. Using a wrong session key - */ - function testFailSessionKeySignatureInvalid() public { - StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account)); - - // Now using Session keys. isValidSignature() should return the MAGIC VALUE too - bytes32 hash = keccak256("Signed by Openfort"); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - address sessionKey2; - uint256 sessionKeyPrivKey2; - (sessionKey2, sessionKeyPrivKey2) = makeAddrAndKey("sessionKey2"); - - // We are now at timestamp 100 - vm.warp(100); - vm.prank(accountAdmin); - openfortAccount.registerSessionKey(sessionKey, 150, 250); // register a session key that is not valid yet - bytes32 domainSeparator; - { - (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = - openfortAccount.eip712Domain(); - console.log(name); - bytes32 _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - domainSeparator = keccak256( - abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) - ); - } - bytes32 hash712 = domainSeparator.toTypedDataHash(hash); - console.logBytes32(hash712); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(sessionKeyPrivKey2, hash712); - bytes memory signature721 = abi.encodePacked(r, s, v); - console.logBytes(signature721); - bytes4 validSig = openfortAccount.isValidSignature(hash, signature721); - console.logBytes4(validSig); - assert(validSig == 0x1626ba7e); // Should fail because the session key is expired - address signer = ecrecover(hash712, v, r, s); - assertEq(sessionKey, signer); // Should [PASS] - assertNotEq(accountAdmin, signer); // Should [PASS] - } - - /* - * Test a complex type using EIP712 Typed Signatures using isValidSignature's (EIP-1271) - */ - function testComplextypeEIP712Signature() public { - assertEq(true, true); // ToDo - } -} diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 29b3920..d0d97e8 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/test/foundry/paymaster/OpenfortPaymasterTest.t.sol b/test/foundry/paymaster/OpenfortPaymasterTest.t.sol deleted file mode 100644 index 94c5e90..0000000 --- a/test/foundry/paymaster/OpenfortPaymasterTest.t.sol +++ /dev/null @@ -1,1313 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; - -import {Test, console} from "lib/forge-std/src/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {EntryPoint, UserOperation, IEntryPoint} from "account-abstraction/core/EntryPoint.sol"; -import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {TestToken} from "account-abstraction/test/TestToken.sol"; -import {IPaymaster} from "account-abstraction/interfaces/IPaymaster.sol"; - -import {StaticOpenfortFactory} from "contracts/core/static/StaticOpenfortFactory.sol"; -import {StaticOpenfortAccount} from "contracts/core/static/StaticOpenfortAccount.sol"; -import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; -import {OpenfortPaymaster} from "contracts/paymaster/OpenfortPaymaster.sol"; - -contract OpenfortPaymasterTest is Test { - using ECDSA for bytes32; - - EntryPoint public entryPoint; - StaticOpenfortAccount public staticOpenfortAccount; - StaticOpenfortFactory public staticOpenfortFactory; - OpenfortPaymaster public openfortPaymaster; - address public account; - TestCounter public testCounter; - TestToken public testToken; - - // Testing addresses - address private factoryAdmin; - uint256 private factoryAdminPKey; - - address private paymasterAdmin; - uint256 private paymasterAdminPKey; - - address private accountAdmin; - uint256 private accountAdminPKey; - - address payable private beneficiary = payable(makeAddr("beneficiary")); - - uint48 internal constant VALIDUNTIL = 2 ** 48 - 1; - uint48 internal constant VALIDAFTER = 0; - uint256 internal constant EXCHANGERATE = 10 ** 3; - uint256 internal constant MOCKSIG = 2 ** 256 - 1; - uint256 internal TESTTOKEN_ACCOUNT_PREFUND = 100 * 10 ** 18; - - error InvalidTokenRecipient(); - - event PostOpGasUpdated(uint256 oldPostOpGas, uint256 _newPostOpGas); - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint, - bytes memory paymasterAndData - ) internal returns (UserOperation[] memory ops) { - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: entryPoint.getNonce(sender, 0), - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 1500000000, - maxPriorityFeePerGas: 1500000000, - paymasterAndData: paymasterAndData, - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - // address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - // address expectedSigner = vm.addr(_signerPKey); - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(_signerPKey)); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData, - bytes memory paymasterAndData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData, - bytes memory paymasterAndData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); - } - - function mockedPaymasterDataNative() internal pure returns (bytes memory dataEncoded) { - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.PayForUser; - strategy.erc20Token = address(0); - strategy.exchangeRate = EXCHANGERATE; - // Looking at the source code, I've found this part was not Packed (filled with 0s) - dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); - } - - function mockedPaymasterDataERC20Dynamic() internal view returns (bytes memory dataEncoded) { - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.DynamicRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - // Looking at the source code, I've found this part was not Packed (filled with 0s) - dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); - } - - function mockedPaymasterDataERC20Fixed(uint256 pricePerTransaction) - internal - view - returns (bytes memory dataEncoded) - { - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.FixedRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = pricePerTransaction; - // Looking at the source code, I've found this part was not Packed (filled with 0s) - dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); - } - - /** - * @notice Initialize the StaticOpenfortAccount testing contract. - * Scenario: - * - factoryAdmin is the deployer (and owner) of the StaticOpenfortFactory - * - paymasterAdmin is the deployer (and owner) of the OpenfortPaymaster - * - accountAdmin is the account used to deploy new static accounts - * - entryPoint is the singleton EntryPoint - * - testCounter is the counter used to test userOps - */ - function setUp() public { - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - (paymasterAdmin, paymasterAdminPKey) = makeAddrAndKey("paymasterAdmin"); - vm.deal(paymasterAdmin, 100 ether); - - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } - vm.prank(paymasterAdmin); - openfortPaymaster = new OpenfortPaymaster(IEntryPoint(payable(address(entryPoint))), paymasterAdmin); - // Paymaster deposits 50 ETH to EntryPoint - vm.prank(paymasterAdmin); - openfortPaymaster.deposit{value: 50 ether}(); - // Paymaster stakes 25 ETH - vm.prank(paymasterAdmin); - openfortPaymaster.addStake{value: 25 ether}(1); - - // deploy account factory - vm.prank(factoryAdmin); - staticOpenfortAccount = new StaticOpenfortAccount(); - vm.prank(factoryAdmin); - staticOpenfortFactory = - new StaticOpenfortFactory((payable(vm.envAddress("ENTRY_POINT_ADDRESS"))), address(staticOpenfortAccount)); - // deploy a new TestCounter - testCounter = new TestCounter(); - // deploy a new TestToken (ERC20) and mint 1000 - testToken = new TestToken(); - testToken.mint(address(this), 1_000 * 10 ** 18); - - // Create an static account wallet and get its address - vm.prank(factoryAdmin); - account = staticOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); - } - - /* - * Test initial parameters - * - */ - function testInitialParameters() public { - assertEq(address(openfortPaymaster.entryPoint()), vm.envAddress("ENTRY_POINT_ADDRESS")); - assertEq(address(openfortPaymaster.owner()), paymasterAdmin); - } - - /* - * Test parsePaymasterAndData() when using the native token - * - */ - function testParsePaymasterDataNative() public { - // Encode the paymaster data - bytes memory dataEncoded = mockedPaymasterDataNative(); - - // Get the related paymaster data signature - bytes32 hash = keccak256(dataEncoded); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - - // Create the paymasterAndData info - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - - ( - uint48 returnedValidUntil, - uint48 returnedValidAfter, - OpenfortPaymaster.PolicyStrategy memory strategy, - bytes memory returnedSignature - ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); - assertEq(returnedValidUntil, VALIDUNTIL); - assertEq(returnedValidAfter, VALIDAFTER); - assertEq(strategy.erc20Token, address(0)); - assertEq(strategy.exchangeRate, EXCHANGERATE); - assertEq(signature, returnedSignature); - } - - /* - * Test parsePaymasterAndData() with an ERC20 dynamic - * - */ - function testParsePaymasterDataERC20() public { - // Encode the paymaster data - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(); - - // Get the related paymaster data signature - bytes32 hash = keccak256(dataEncoded); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - - // Create the paymasterAndData info - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - console.logBytes(paymasterAndData); - - ( - uint48 returnedValidUntil, - uint48 returnedValidAfter, - OpenfortPaymaster.PolicyStrategy memory strategy, - bytes memory returnedSignature - ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); - assertEq(returnedValidUntil, VALIDUNTIL); - assertEq(returnedValidAfter, VALIDAFTER); - assertEq(strategy.erc20Token, address(testToken)); - assertEq(strategy.exchangeRate, EXCHANGERATE); - assertEq(signature, returnedSignature); - } - - /* - * The owner (paymasterAdmin) can add and withdraw stake. - * Others cannot. - */ - function testPaymasterStake() public { - assertEq(paymasterAdmin.balance, 25 ether); - - // The owner can add stake - vm.prank(paymasterAdmin); - openfortPaymaster.addStake{value: 2 ether}(10); - assertEq(paymasterAdmin.balance, 23 ether); - - // Others cannot add stake - vm.expectRevert("Ownable: caller is not the owner"); - openfortPaymaster.addStake{value: 2}(10); - - // The owner trying to withdraw stake fails because it has not unlocked - // The owner can withdraw stake - vm.prank(paymasterAdmin); - vm.expectRevert(); - openfortPaymaster.withdrawStake(payable(paymasterAdmin)); - - // The owner unlocks the stake - vm.prank(paymasterAdmin); - openfortPaymaster.unlockStake(); - - // The owner trying to unlock fails because it has not passed enought time - vm.prank(paymasterAdmin); - vm.expectRevert(); - openfortPaymaster.withdrawStake(payable(paymasterAdmin)); - - // Passes 20 blocks... - skip(20); - - // The owner can now withdraw stake (the 2 ethers recently staked + the 25 from the SetUp) - vm.prank(paymasterAdmin); - openfortPaymaster.withdrawStake(payable(paymasterAdmin)); - assertEq(paymasterAdmin.balance, 50 ether); - } - - /* - * Deposit 2 ETH to the EntryPoint on Paymaster's behalf - * - */ - function testEntryPointDepositToPaymaster() public { - assert(entryPoint.balanceOf(address(openfortPaymaster)) == 50 ether); - - // Directly deposit 1 ETH to EntryPoint on behalf of paymaster - entryPoint.depositTo{value: 1 ether}(address(openfortPaymaster)); - assert(entryPoint.balanceOf(address(openfortPaymaster)) == 51 ether); - - // Paymaster deposits 1 ETH to EntryPoint - openfortPaymaster.deposit{value: 1 ether}(); - assert(openfortPaymaster.getDeposit() == 52 ether); - } - - /* - * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig length) - * Should revert - */ - function testPaymasterUserOpWrongSigLength() public { - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, "0x1234"); // This part was packed (not filled with 0s) - - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testCounter), - 0, - abi.encodeWithSignature("count()"), - paymasterAndData - ); - - // "ECDSA: invalid signature length" - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig) - * Should revert - */ - function testPaymasterUserOpWrongSig() public { - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); // MOCKSIG, "1", MOCKSIG to make sure we send 65 bytes as sig - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testCounter), - 0, - abi.encodeWithSignature("count()"), - paymasterAndData - ); - - // "AA33 reverted: ECDSA: invalid signature" - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - - // Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * Should work - */ - function testPaymasterUserOpNativeValidSig() public { - bytes memory dataEncoded = mockedPaymasterDataNative(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testCounter), - 0, - abi.encodeWithSignature("count()"), - paymasterAndData - ); - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.PayForUser; - strategy.erc20Token = address(0); - strategy.exchangeRate = EXCHANGERATE; - bytes32 hash; - { - // Simulating that the Paymaster gets the userOp and signs it - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - // entryPoint.simulateValidation(userOp); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - //Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with signature from a wrong address - * Should not work - */ - function testPaymasterUserOpNativeWrongUserSig() public { - bytes memory dataEncoded = mockedPaymasterDataNative(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testCounter), - 0, - abi.encodeWithSignature("count()"), - paymasterAndData - ); - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.PayForUser; - strategy.erc20Token = address(0); - strategy.exchangeRate = EXCHANGERATE; - bytes32 hash; - { - // Simulating that the factory admin gets the userOp and tries to sign it - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(factoryAdmin, ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(factoryAdminPKey)); - - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - vm.expectRevert(); - entryPoint.handleOps(userOps, beneficiary); - // entryPoint.simulateValidation(userOp); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); - //Verifiy that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * Using ERC20. Should work - */ - function testPaymasterUserOpERC20ValidSigDiffMaxPriorityFeePerGas() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), - paymasterAndData - ); - - userOps[0].maxPriorityFeePerGas += 1; - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.DynamicRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased - assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * Using ERC20. Should work - */ - function testPaymasterUserOpERC20ValidSig() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), - paymasterAndData - ); - - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.DynamicRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased - assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * Using FIXED ERC20. Should work - */ - function testPaymasterUserOpERC20FixedValidSig() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - uint256 pricePerTransaction = 10 ** 18; - - bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(pricePerTransaction); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), - paymasterAndData - ); - - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.FixedRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = pricePerTransaction; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased - assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Using dynamic ERC20. Should work - */ - function testPaymasterUserOpERC20ValidSigExecBatch() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - assertEq(testCounter.counters(account), 0); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.DynamicRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased - assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Using fixed ERC20. Should work - */ - function testPaymasterUserOpERC20FixedValidSigExecBatch() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - assertEq(testCounter.counters(account), 0); - - uint256 pricePerTransaction = 10 ** 18; - - bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(pricePerTransaction); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.FixedRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = pricePerTransaction; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased - assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Using fixed ERC20 expensive. Should work - */ - function testPaymasterUserOpERC20FixedExpensiveValidSigExecBatch() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - assertEq(testCounter.counters(account), 0); - - uint256 pricePerTransaction = 10; - - bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(pricePerTransaction); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.FixedRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = pricePerTransaction; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased - assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Should work - */ - function testPaymasterUserOpNativeValidSigExecBatch() public { - bytes memory dataEncoded = mockedPaymasterDataNative(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.PayForUser; - strategy.erc20Token = address(0); - strategy.exchangeRate = EXCHANGERATE; - - bytes32 hash; - { - // Simulating that the Paymaster gets the userOp and signs it - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - // entryPoint.simulateValidation(userOp); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - //Verifiy that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Using ERC20. Should work. - * Test showing that failing to repay in ERC20 still spends some of Paymaster's deposit (DoS) - */ - function testFailPaymasterUserOpERC20ValidSigExecBatchInsufficientERC20() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, 100); - assertEq(testToken.balanceOf(account), 100); - - assertEq(testCounter.counters(account), 0); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.DynamicRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has the same deposit - assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has not decreased - assertEq(testToken.balanceOf(account), 100); - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - - // If this fails, it would mean: - // 1- That the paymaster has spent some of its deposit - // 2- That the smart account could not perform the desired actions, but still has all testTokens - // An attacker could DoS the paymaster to drain its deposit - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * Using ERC20. Should work. - */ - function testFailPaymasterUserOpERC20ValidSigSmallApprove() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 1), - paymasterAndData - ); - - OpenfortPaymaster.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymaster.Mode.DynamicRate; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has the same deposit - assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has not decreased - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - - // If this fails, it would mean: - // 1- That the paymaster has spent some of its deposit - // 2- That the smart account could not perform the desired actions, but still has all testTokens - // An attacker could DoS the paymaster to drain its deposit - } - - /* - * Test UpdateTokenRecipient function - */ - function testUpdateTokenRecipient() public { - assertEq(openfortPaymaster.tokenRecipient(), paymasterAdmin); - vm.expectRevert("Ownable: caller is not the owner"); - openfortPaymaster.updateTokenRecipient(factoryAdmin); - - vm.prank(paymasterAdmin); - vm.expectRevert(OpenfortErrorsAndEvents.ZeroValueNotAllowed.selector); - openfortPaymaster.updateTokenRecipient(address(0)); - - vm.prank(paymasterAdmin); - openfortPaymaster.updateTokenRecipient(factoryAdmin); - assertEq(openfortPaymaster.tokenRecipient(), factoryAdmin); - } - - /* - * Test WithdrawTo function - */ - function testWithdrawTo() public { - assertEq(openfortPaymaster.getDeposit(), 50 ether); - - vm.prank(factoryAdmin); - vm.expectRevert("Ownable: caller is not the owner"); - openfortPaymaster.withdrawTo(payable(paymasterAdmin), 5 ether); - assertEq(openfortPaymaster.getDeposit(), 50 ether); - - vm.prank(paymasterAdmin); - vm.expectRevert("Withdraw amount too large"); - openfortPaymaster.withdrawTo(payable(paymasterAdmin), 5000 ether); - assertEq(openfortPaymaster.getDeposit(), 50 ether); - - vm.prank(paymasterAdmin); - openfortPaymaster.withdrawTo(payable(paymasterAdmin), 5 ether); - assertEq(openfortPaymaster.getDeposit(), 45 ether); - - vm.prank(paymasterAdmin); - openfortPaymaster.deposit{value: 1 ether}(); - assertEq(openfortPaymaster.getDeposit(), 46 ether); - } - - /* - * Test setPostOpGas function - */ - function testSetPostOpGas() public { - vm.expectRevert("Ownable: caller is not the owner"); - openfortPaymaster.setPostOpGas(15_000); - - vm.prank(paymasterAdmin); - vm.expectRevert(); - openfortPaymaster.setPostOpGas(0); - - // Expect that we will see a PostOpGasUpdated event - vm.prank(paymasterAdmin); - vm.expectEmit(true, true, false, false); - emit PostOpGasUpdated(40_000, 15_000); - openfortPaymaster.setPostOpGas(15_000); - } - - /* - * Trigger _requireFromEntryPoint() from BaseOpenfortPaymaster - */ - function test_requireFromEntryPoint() public { - UserOperation[] memory userOpAux = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()"), "" - ); - - vm.prank(paymasterAdmin); - vm.expectRevert("Sender not EntryPoint"); - openfortPaymaster.validatePaymasterUserOp(userOpAux[0], bytes32(""), 0); - - vm.prank(paymasterAdmin); - vm.expectRevert("Sender not EntryPoint"); - openfortPaymaster.postOp(IPaymaster.PostOpMode(0), bytes(""), 0); - } -} diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index a61d083..b546ca2 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.19; +pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; From edcda41adf94af876738be4b104838bcce50813e Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 13 Nov 2023 13:48:17 +0100 Subject: [PATCH 05/83] Adapting ERC6551 & removing old files --- .env.example | 1 + script/UserOpTestCounter.sol | 11 +++++++---- script/deployBatching.s.sol | 10 +++++----- script/deployEIP6551.sol | 6 ++---- script/deployMock.sol | 2 +- test/foundry/4337/Specific4337Tests.t.sol | 16 ++++++++-------- .../eip6551/EIP6551OpenfortAccountTest.t.sol | 6 +++--- .../core/eip6551/EIP6551OpenfortBenchmark.t.sol | 6 +++--- .../paymaster/OpenfortPaymasterV2Test.t.sol | 16 ++++++++-------- 9 files changed, 38 insertions(+), 36 deletions(-) diff --git a/.env.example b/.env.example index 262fd86..c2f107b 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ # Entry point v0.6.0 ENTRY_POINT_ADDRESS="0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" +ERC6551_REGISTRY_ADDRESS="0x000000006551c19487814612e58FE06813775758" PAYMASTER_ADDRESS= MNEMONIC="test test test test test test test test test test test junk" VERSION_SALT=0x00 diff --git a/script/UserOpTestCounter.sol b/script/UserOpTestCounter.sol index 5dcedc5..08b8b4e 100644 --- a/script/UserOpTestCounter.sol +++ b/script/UserOpTestCounter.sol @@ -3,7 +3,10 @@ pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {StaticOpenfortFactory, StaticOpenfortAccount} from "../contracts/core/static/StaticOpenfortFactory.sol"; +import { + UpgradeableOpenfortFactory, + UpgradeableOpenfortAccount +} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {EntryPoint} from "account-abstraction/core/EntryPoint.sol"; import {UserOperation, UserOperationLib} from "account-abstraction/interfaces/UserOperation.sol"; @@ -18,8 +21,8 @@ contract UserOpTestCounter is Script { address internal deployAddress = vm.addr(deployPrivKey); EntryPoint internal entryPoint = EntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - StaticOpenfortFactory staticOpenfortFactory; - StaticOpenfortAccount staticOpenfortAccount; + UpgradeableOpenfortFactory staticOpenfortFactory; + UpgradeableOpenfortAccount staticOpenfortAccount; TestCounter testCounter; // WIP based on calcPreVerificationGas.ts from the bundler's SDK @@ -117,7 +120,7 @@ contract UserOpTestCounter is Script { vm.selectFork(mumbaiFork); // Due to errors with Foundry and create2, let's use hardcoded addresses for testing: - staticOpenfortFactory = StaticOpenfortFactory(0xe9B5fb44f377Ce5a03427d5Be7D9d073bf8FE1f0); + staticOpenfortFactory = UpgradeableOpenfortFactory(0xe9B5fb44f377Ce5a03427d5Be7D9d073bf8FE1f0); testCounter = new TestCounter(); } diff --git a/script/deployBatching.s.sol b/script/deployBatching.s.sol index 8cac5de..621f2a3 100644 --- a/script/deployBatching.s.sol +++ b/script/deployBatching.s.sol @@ -5,12 +5,12 @@ import {Script, console} from "forge-std/Script.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {UserOperation, EntryPoint} from "account-abstraction/core/EntryPoint.sol"; -import {StaticOpenfortFactory} from "../contracts/core/static/StaticOpenfortFactory.sol"; +import {UpgradeableOpenfortFactory} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; contract DeployBatching is Script { using ECDSA for bytes32; - StaticOpenfortFactory public staticOpenfortFactory; + UpgradeableOpenfortFactory public staticOpenfortFactory; TestCounter public testCounter; uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); @@ -86,14 +86,14 @@ contract DeployBatching is Script { // Due to errors with Foundry and create2, let's use hardcoded addresses for testing: // Created with - // forge create StaticOpenfortFactory --mnemonic $MNEMONIC --constructor-args $ENTRY_POINT_ADDRESS --rpc-url $POLYGON_MUMBAI_RPC --verify - staticOpenfortFactory = StaticOpenfortFactory(0xe9B5fb44f377Ce5a03427d5Be7D9d073bf8FE1f0); + // forge create UpgradeableOpenfortFactory --mnemonic $MNEMONIC --constructor-args $ENTRY_POINT_ADDRESS --rpc-url $POLYGON_MUMBAI_RPC --verify + staticOpenfortFactory = UpgradeableOpenfortFactory(0xe9B5fb44f377Ce5a03427d5Be7D9d073bf8FE1f0); // Created with // forge create TestCounter --mnemonic $MNEMONIC --rpc-url $POLYGON_MUMBAI_RPC --verify testCounter = TestCounter(0x1A09053F78695ad7372D0539E5246d025b254A4c); // Created with: - // $forge create StaticOpenfortAccount --constructor-args $ENTRY_POINT_ADDRESS 0x6E767F52d49b0abD686003727b8bc0684011819B --mnemonic $MNEMONIC --rpc-url $POLYGON_MUMBAI_RPC --verify + // $forge create UpgradeableOpenfortAccount --constructor-args $ENTRY_POINT_ADDRESS 0x6E767F52d49b0abD686003727b8bc0684011819B --mnemonic $MNEMONIC --rpc-url $POLYGON_MUMBAI_RPC --verify address account = staticOpenfortFactory.createAccountWithNonce(deployAddress, "1"); uint256 count = 3; diff --git a/script/deployEIP6551.sol b/script/deployEIP6551.sol index c30def5..3d6b5cf 100644 --- a/script/deployEIP6551.sol +++ b/script/deployEIP6551.sol @@ -5,12 +5,13 @@ import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; import {VIPNFT} from "contracts/mock/VipNFT.sol"; import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; -import {ERC6551Registry} from "../contracts/core/eip6551/ERC6551Registry.sol"; +import {IERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; contract EIP6551OpenfortDeploy is Script { uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); address internal deployAddress = vm.addr(deployPrivKey); IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); + IERC6551Registry internal erc6551Registry = IERC6551Registry((payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS")))); VIPNFT testToken; function run() public { @@ -20,9 +21,6 @@ contract EIP6551OpenfortDeploy is Script { // Create an acccount to serve as implementation EIP6551OpenfortAccount eip6551OpenfortAccount = new EIP6551OpenfortAccount{salt: versionSalt}(); - // Create a factory to deploy cloned accounts - ERC6551Registry erc6551Registry = new ERC6551Registry{salt: versionSalt}(); - uint256 chainId; assembly { chainId := chainid() diff --git a/script/deployMock.sol b/script/deployMock.sol index f0866a6..71fb6e7 100644 --- a/script/deployMock.sol +++ b/script/deployMock.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; -import {StaticOpenfortFactory} from "../contracts/core/static/StaticOpenfortFactory.sol"; +import {UpgradeableOpenfortFactory} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; // import {USDC} from "../contracts/mock/USDC.sol"; import {Rewards} from "../contracts/mock/Rewards.sol"; diff --git a/test/foundry/4337/Specific4337Tests.t.sol b/test/foundry/4337/Specific4337Tests.t.sol index 705ffec..e2028d3 100644 --- a/test/foundry/4337/Specific4337Tests.t.sol +++ b/test/foundry/4337/Specific4337Tests.t.sol @@ -6,8 +6,8 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {TestToken} from "account-abstraction/test/TestToken.sol"; -import {StaticOpenfortFactory} from "contracts/core/static/StaticOpenfortFactory.sol"; -import {StaticOpenfortAccount} from "contracts/core/static/StaticOpenfortAccount.sol"; +import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; +import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import "account-abstraction/core/Helpers.sol" as Helpers; contract Specific4337Tests is Test { @@ -16,7 +16,7 @@ contract Specific4337Tests is Test { uint48 constant MAX_TIME = 2 ** 48 - 1; EntryPoint public entryPoint; - StaticOpenfortFactory public staticOpenfortFactory; + UpgradeableOpenfortFactory public staticOpenfortFactory; TestCounter public testCounter; TestToken public testToken; @@ -106,13 +106,13 @@ contract Specific4337Tests is Test { bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOp[0]); vm.prank(address(entryPoint)); - return StaticOpenfortAccount(payable(sender)).validateUserOp(userOp[0], opHash, 0); + return UpgradeableOpenfortAccount(payable(sender)).validateUserOp(userOp[0], opHash, 0); } /** - * @notice Initialize the StaticOpenfortAccount testing contract. + * @notice Initialize the UpgradeableOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the StaticOpenfortFactory + * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory * - accountAdmin is the account used to deploy new static accounts * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps @@ -183,7 +183,7 @@ contract Specific4337Tests is Test { // (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); // vm.prank(accountAdmin); - // StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, MAX_TIME); + // UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, MAX_TIME); // uint256 validationData = _getValidationData( // account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -210,7 +210,7 @@ contract Specific4337Tests is Test { // (sessionKey2, sessionKeyPrivKey2) = makeAddrAndKey("sessionKey2"); // vm.prank(accountAdmin); - // StaticOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, MAX_TIME); + // UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, MAX_TIME); // uint256 validationData = _getValidationData( // account, sessionKeyPrivKey2, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") diff --git a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol index 0298683..d66d817 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol @@ -6,7 +6,7 @@ import {SigUtils} from "../../utils/SigUtils.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {VIPNFT} from "contracts/mock/VipNFT.sol"; -import {ERC6551Registry} from "contracts/core/eip6551/ERC6551Registry.sol"; +import {ERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; contract EIP6551OpenfortAccountTest is Test { @@ -113,9 +113,9 @@ contract EIP6551OpenfortAccountTest is Test { } /** - * @notice Initialize the StaticOpenfortAccount testing contract. + * @notice Initialize the UpgradeableOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the StaticOpenfortFactory + * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory * - accountAdmin is the account used to deploy new static accounts * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps diff --git a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol index 77d1936..78a9d5f 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol @@ -11,7 +11,7 @@ import {USDC} from "contracts/mock/USDC.sol"; import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; -import {ERC6551Registry} from "contracts/core/eip6551/ERC6551Registry.sol"; +import {ERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; contract EIP6551OpenfortBenchmark is Test { @@ -132,9 +132,9 @@ contract EIP6551OpenfortBenchmark is Test { } /** - * @notice Initialize the StaticOpenfortAccount testing contract. + * @notice Initialize the UpgradeableOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the StaticOpenfortFactory + * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory * - accountAdmin is the account used to deploy new static accounts * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index b546ca2..8c4a9bc 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -8,16 +8,16 @@ import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {TestToken} from "account-abstraction/test/TestToken.sol"; import {IPaymaster} from "account-abstraction/interfaces/IPaymaster.sol"; -import {StaticOpenfortFactory} from "contracts/core/static/StaticOpenfortFactory.sol"; -import {StaticOpenfortAccount} from "contracts/core/static/StaticOpenfortAccount.sol"; +import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; +import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {OpenfortPaymasterV2} from "contracts/paymaster/OpenfortPaymasterV2.sol"; contract OpenfortPaymasterV2Test is Test { using ECDSA for bytes32; EntryPoint public entryPoint; - StaticOpenfortAccount public staticOpenfortAccount; - StaticOpenfortFactory public staticOpenfortFactory; + UpgradeableOpenfortAccount public staticOpenfortAccount; + UpgradeableOpenfortFactory public staticOpenfortFactory; OpenfortPaymasterV2 public openfortPaymaster; address public account; TestCounter public testCounter; @@ -162,9 +162,9 @@ contract OpenfortPaymasterV2Test is Test { } /** - * @notice Initialize the StaticOpenfortAccount testing contract. + * @notice Initialize the UpgradeableOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the StaticOpenfortFactory + * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory * - paymasterAdmin is the deployer (and owner) of the OpenfortPaymaster * - accountAdmin is the account used to deploy new static accounts * - entryPoint is the singleton EntryPoint @@ -202,10 +202,10 @@ contract OpenfortPaymasterV2Test is Test { // deploy account factory vm.prank(factoryAdmin); - staticOpenfortAccount = new StaticOpenfortAccount(); + staticOpenfortAccount = new UpgradeableOpenfortAccount(); vm.prank(factoryAdmin); staticOpenfortFactory = - new StaticOpenfortFactory((payable(vm.envAddress("ENTRY_POINT_ADDRESS"))), address(staticOpenfortAccount)); + new UpgradeableOpenfortFactory((payable(vm.envAddress("ENTRY_POINT_ADDRESS"))), address(staticOpenfortAccount)); // deploy a new TestCounter testCounter = new TestCounter(); // deploy a new TestToken (ERC20) and mint 1000 From 7a9ad2dd98bc5afeb2ff89e2196cf3a80958139d Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 13 Nov 2023 13:48:31 +0100 Subject: [PATCH 06/83] More cleanup --- .../eip6551/interfaces/IERC6551Account.sol | 52 -------- contracts/core/eip6551/utils/Bytecode.sol | 74 ----------- script/Utils/GasCalculations.sol | 14 -- script/debugUserOp.s.sol | 2 +- script/deployAllChains.sh | 22 ---- script/deployBatching.s.sol | 120 ------------------ script/deployEIP6551.sol | 12 +- .../eip6551/EIP6551OpenfortAccountTest.t.sol | 10 +- test/foundry/utils/SigUtils.sol | 2 +- 9 files changed, 8 insertions(+), 300 deletions(-) delete mode 100644 contracts/core/eip6551/interfaces/IERC6551Account.sol delete mode 100644 contracts/core/eip6551/utils/Bytecode.sol delete mode 100644 script/Utils/GasCalculations.sol delete mode 100644 script/deployBatching.s.sol diff --git a/contracts/core/eip6551/interfaces/IERC6551Account.sol b/contracts/core/eip6551/interfaces/IERC6551Account.sol deleted file mode 100644 index ea2ac53..0000000 --- a/contracts/core/eip6551/interfaces/IERC6551Account.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -/// @dev the ERC-165 identifier for this interface is `0x400a0398` -interface IERC6551Account { - /// @dev Token bound accounts MUST implement a `receive` function. - /// - /// Token bound accounts MAY perform arbitrary logic to restrict conditions - /// under which Ether can be received. - receive() external payable; - - /// @dev Executes `call` on address `to`, with value `value` and calldata - /// `data`. - /// - /// MUST revert and bubble up errors if call fails. - /// - /// By default, token bound accounts MUST allow the owner of the ERC-721 token - /// which owns the account to execute arbitrary calls using `executeCall`. - /// - /// Token bound accounts MAY implement additional authorization mechanisms - /// which limit the ability of the ERC-721 token holder to execute calls. - /// - /// Token bound accounts MAY implement additional execution functions which - /// grant execution permissions to other non-owner accounts. - /// - /// @return The result of the call - function executeCall(address to, uint256 value, bytes calldata data) external payable returns (bytes memory); - - /// @dev Returns identifier of the ERC-721 token which owns the - /// account - /// - /// The return value of this function MUST be constant - it MUST NOT change - /// over time. - /// - /// @return chainId The EIP-155 ID of the chain the ERC-721 token exists on - /// @return tokenContract The contract address of the ERC-721 token - /// @return tokenId The ID of the ERC-721 token - function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId); - - /// @dev Returns the owner of the ERC-721 token which controls the account - /// if the token exists. - /// - /// This is value is obtained by calling `ownerOf` on the ERC-721 contract. - /// - /// @return Address of the owner of the ERC-721 token which owns the account - function owner() external view returns (address); - - /// @dev Returns a nonce value that is updated on every successful transaction - /// - /// @return The current account nonce - function nonce() external view returns (uint256); -} diff --git a/contracts/core/eip6551/utils/Bytecode.sol b/contracts/core/eip6551/utils/Bytecode.sol deleted file mode 100644 index 704ca5b..0000000 --- a/contracts/core/eip6551/utils/Bytecode.sol +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -library Bytecode { - error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end); - - /** - * @notice Generate a creation code that results on a contract with `_code` as bytecode - * @param _code The returning value of the resulting `creationCode` - * @return creationCode (constructor) for new contract - */ - function creationCodeFor(bytes memory _code) internal pure returns (bytes memory) { - /* - 0x00 0x63 0x63XXXXXX PUSH4 _code.length size - 0x01 0x80 0x80 DUP1 size size - 0x02 0x60 0x600e PUSH1 14 14 size size - 0x03 0x60 0x6000 PUSH1 00 0 14 size size - 0x04 0x39 0x39 CODECOPY size - 0x05 0x60 0x6000 PUSH1 00 0 size - 0x06 0xf3 0xf3 RETURN - - */ - - return abi.encodePacked(hex"63", uint32(_code.length), hex"80600E6000396000F3", _code); - } - - /** - * @notice Returns the size of the code on a given address - * @param _addr Address that may or may not contain code - * @return size of the code on the given `_addr` - */ - function codeSize(address _addr) internal view returns (uint256 size) { - assembly { - size := extcodesize(_addr) - } - } - - /** - * @notice Returns the code of a given address - * @dev It will fail if `_end < _start` - * @param _addr Address that may or may not contain code - * @param _start number of bytes of code to skip on read - * @param _end index before which to end extraction - * @return oCode read from `_addr` deployed bytecode - * - * Forked from: https://gist.github.com/KardanovIR/fe98661df9338c842b4a30306d507fbd - */ - function codeAt(address _addr, uint256 _start, uint256 _end) internal view returns (bytes memory oCode) { - uint256 csize = codeSize(_addr); - if (csize == 0) return bytes(""); - - if (_start > csize) return bytes(""); - if (_end < _start) revert InvalidCodeAtRange(csize, _start, _end); - - unchecked { - uint256 reqSize = _end - _start; - uint256 maxSize = csize - _start; - - uint256 size = maxSize < reqSize ? maxSize : reqSize; - - assembly { - // allocate output byte array - this could also be done without assembly - // by using o_code = new bytes(size) - oCode := mload(0x40) - // new "memory end" including padding - mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f)))) - // store length in memory - mstore(oCode, size) - // actually retrieve the code, this needs assembly - extcodecopy(_addr, add(oCode, 0x20), _start, size) - } - } - } -} diff --git a/script/Utils/GasCalculations.sol b/script/Utils/GasCalculations.sol deleted file mode 100644 index dfb7d14..0000000 --- a/script/Utils/GasCalculations.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.17; - -import {UserOperation, UserOperationLib} from "lib/account-abstraction/contracts/interfaces/UserOperation.sol"; - -using UserOperationLib for UserOperation; - -struct DefaultGasOverheads { - uint256 fixedCost; -} - -function calcPreVerificationGas(UserOperation calldata userOp) pure { - userOp.pack(); -} diff --git a/script/debugUserOp.s.sol b/script/debugUserOp.s.sol index 77949d6..33372d6 100644 --- a/script/debugUserOp.s.sol +++ b/script/debugUserOp.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.19; +pragma solidity =0.8.19; import "forge-std/Script.sol"; import {UserOperation, EntryPoint} from "account-abstraction/core/EntryPoint.sol"; diff --git a/script/deployAllChains.sh b/script/deployAllChains.sh index dc72c27..17f5bad 100755 --- a/script/deployAllChains.sh +++ b/script/deployAllChains.sh @@ -3,28 +3,6 @@ source .env LOG_FILE=script/deployments/$(date +%Y-%m-%d_%H:%M)"-deploymentAllChains.log" -# echo "------ StaticOpenfortDeploy ------ (Goerli)" -# forge script StaticOpenfortDeploy --rpc-url $GOERLI_RPC -vvvv --verify --broadcast --slow --etherscan-api-key $GOERLI_API_KEY >> $LOG_FILE -# sleep 3 -# echo "------ StaticOpenfortDeploy ------ (Mumbai)" -# forge script StaticOpenfortDeploy --rpc-url $POLYGON_MUMBAI_RPC -vvvv --verify --broadcast --slow --etherscan-api-key $POLYGON_MUMBAI_KEY >> $LOG_FILE -# sleep 3 -# echo "------ StaticOpenfortDeploy ------ (Fuji)" -# forge script StaticOpenfortDeploy --rpc-url $AVALANCHE_FUJI_RPC -vvvv --verify --broadcast --slow --etherscan-api-key $FUJI_API_KEY >> $LOG_FILE -# sleep 3 -# echo "------ StaticOpenfortDeploy ------ (BSC testnet)" -# forge script StaticOpenfortDeploy --rpc-url $BSC_TESTNET_RPC -vvvv --verify --broadcast --slow --etherscan-api-key $BSCSCAN_TESTNET_API_KEY >> $LOG_FILE -# sleep 3 -# echo "------ StaticOpenfortDeploy ------ (Arbitrum Goerli testnet)" -# forge script StaticOpenfortDeploy --rpc-url $ARBITRUM_GOERLI_RPC -vvvv --verify --broadcast --slow --etherscan-api-key $ARBISCAN_API_KEY >> $LOG_FILE -# sleep 3 -# echo "------ StaticOpenfortDeploy ------ (Gnosis Chiado testnet)" -# forge script StaticOpenfortDeploy --rpc-url $GNOSIS_CHIADO_RPC -vvvv --verify --broadcast --slow --etherscan-api-key $GNOSIS_API_KEY_BLOCKSCOUT >> $LOG_FILE -# sleep 3 -# echo "------ StaticOpenfortDeploy ------ (Base Goerli)" -# forge script StaticOpenfortDeploy --rpc-url $BASE_TEST_RPC -vvvv --verify --broadcast --slow --etherscan-api-key $BASE_API_KEY >> $LOG_FILE -# sleep 3 - # echo "------ UpgradeableOpenfortDeploy ------ (Goerli)" # forge script UpgradeableOpenfortDeploy --rpc-url $GOERLI_RPC -vvvv --verify --broadcast --slow --etherscan-api-key $GOERLI_API_KEY >> $LOG_FILE # sleep 3 diff --git a/script/deployBatching.s.sol b/script/deployBatching.s.sol deleted file mode 100644 index 621f2a3..0000000 --- a/script/deployBatching.s.sol +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -import {Script, console} from "forge-std/Script.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {UserOperation, EntryPoint} from "account-abstraction/core/EntryPoint.sol"; -import {UpgradeableOpenfortFactory} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; - -contract DeployBatching is Script { - using ECDSA for bytes32; - - UpgradeableOpenfortFactory public staticOpenfortFactory; - TestCounter public testCounter; - - uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); - address internal deployAddress = vm.addr(deployPrivKey); - EntryPoint internal entryPoint = EntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - - function setUp() public {} - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal view returns (UserOperation[] memory ops) { - uint256 nonce = entryPoint.getNonce(sender, 0); - - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 0, - maxPriorityFeePerGas: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assert(recoveredSigner == expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal view returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - function run() public { - vm.startBroadcast(deployPrivKey); - - // Due to errors with Foundry and create2, let's use hardcoded addresses for testing: - // Created with - // forge create UpgradeableOpenfortFactory --mnemonic $MNEMONIC --constructor-args $ENTRY_POINT_ADDRESS --rpc-url $POLYGON_MUMBAI_RPC --verify - staticOpenfortFactory = UpgradeableOpenfortFactory(0xe9B5fb44f377Ce5a03427d5Be7D9d073bf8FE1f0); - // Created with - // forge create TestCounter --mnemonic $MNEMONIC --rpc-url $POLYGON_MUMBAI_RPC --verify - testCounter = TestCounter(0x1A09053F78695ad7372D0539E5246d025b254A4c); - - // Created with: - // $forge create UpgradeableOpenfortAccount --constructor-args $ENTRY_POINT_ADDRESS 0x6E767F52d49b0abD686003727b8bc0684011819B --mnemonic $MNEMONIC --rpc-url $POLYGON_MUMBAI_RPC --verify - address account = staticOpenfortFactory.createAccountWithNonce(deployAddress, "1"); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, deployPrivKey, bytes(""), targets, values, callData); - - entryPoint.depositTo{value: 10000000000000000}(account); - entryPoint.handleOps(userOp, payable(deployAddress)); - - console.log(testCounter.counters(account)); - - vm.stopBroadcast(); - } -} diff --git a/script/deployEIP6551.sol b/script/deployEIP6551.sol index 3d6b5cf..70a711e 100644 --- a/script/deployEIP6551.sol +++ b/script/deployEIP6551.sol @@ -12,7 +12,7 @@ contract EIP6551OpenfortDeploy is Script { address internal deployAddress = vm.addr(deployPrivKey); IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); IERC6551Registry internal erc6551Registry = IERC6551Registry((payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS")))); - VIPNFT testToken; + VIPNFT internal testToken; function run() public { bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); @@ -30,14 +30,8 @@ contract EIP6551OpenfortDeploy is Script { testToken = new VIPNFT(); // The first call should create a new account, while the second will just return the corresponding account address - address account2 = erc6551Registry.createAccount( - address(eip6551OpenfortAccount), - chainId, - address(testToken), - 1, - 1, - abi.encodeWithSignature("initialize(address)", address(entryPoint)) - ); + address account2 = + erc6551Registry.createAccount(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1); console.log("Registry at address %s has created an account at address %s", address(erc6551Registry), account2); vm.stopBroadcast(); diff --git a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol index d66d817..8a86466 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol @@ -6,7 +6,7 @@ import {SigUtils} from "../../utils/SigUtils.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {VIPNFT} from "contracts/mock/VipNFT.sol"; -import {ERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; +import {ERC6551Registry, ERC6551AccountCreated} from "lib/erc6551/src/ERC6551Registry.sol"; import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; contract EIP6551OpenfortAccountTest is Test { @@ -28,10 +28,6 @@ contract EIP6551OpenfortAccountTest is Test { address payable private beneficiary = payable(makeAddr("beneficiary")); - event AccountCreated( - address account, address implementation, uint256 chainId, address tokenContract, uint256 tokenId, uint256 salt - ); - /* * Auxiliary function to generate a userOP */ @@ -113,7 +109,7 @@ contract EIP6551OpenfortAccountTest is Test { } /** - * @notice Initialize the UpgradeableOpenfortAccount testing contract. + * @notice Initialize the StaticOpenfortAccount testing contract. * Scenario: * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory * - accountAdmin is the account used to deploy new static accounts @@ -308,7 +304,7 @@ contract EIP6551OpenfortAccountTest is Test { // Expect that we will see an event containing the account and admin vm.expectEmit(true, true, false, true); - emit AccountCreated( + emit ERC6551AccountCreated( eip6551OpenfortAccountAddress2, address(eip6551OpenfortAccount), chainId, address(testToken), 1, 2 ); diff --git a/test/foundry/utils/SigUtils.sol b/test/foundry/utils/SigUtils.sol index 7cfdbdb..85099a8 100644 --- a/test/foundry/utils/SigUtils.sol +++ b/test/foundry/utils/SigUtils.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.19; +pragma solidity =0.8.19; /* * Based on Foundry's tutorial: https://book.getfoundry.sh/tutorials/testing-eip712 From dee81587628f032dfaab83bc2268d1cc0cd08cd5 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 13 Nov 2023 16:58:17 +0100 Subject: [PATCH 07/83] updated checkDeposits --- script/OpenfortForksConfig.s.sol | 1 + script/checkDeposits.s.sol | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/script/OpenfortForksConfig.s.sol b/script/OpenfortForksConfig.s.sol index 3df259c..2a7f163 100644 --- a/script/OpenfortForksConfig.s.sol +++ b/script/OpenfortForksConfig.s.sol @@ -51,6 +51,7 @@ abstract contract OpenfortForksConfig is Script { uint256 internal constant ARBITRUM_MAIN = 42161; uint256 internal constant ARBITRUM_NOVA = 42170; uint256 internal constant BEAM_MAIN = 4337; + uint256 internal constant BEAM_TESTNET_MAIN = 13337; constructor() { /*////////////////////////////////////////////////////////////////////////// diff --git a/script/checkDeposits.s.sol b/script/checkDeposits.s.sol index 5a62cb1..b80fffa 100644 --- a/script/checkDeposits.s.sol +++ b/script/checkDeposits.s.sol @@ -26,7 +26,7 @@ contract CheckDeposits is OpenfortForksConfig { if (paymasterDeposit < 0.1 ether) { console.log("ALERT: deposit too low on chain ID %s! Deposit: %s\n", block.chainid, paymasterDeposit); } - } else if (block.chainid == BEAM_MAIN) { + } else if (block.chainid == BEAM_MAIN || block.chainid == BEAM_TESTNET_MAIN) { if (paymasterDeposit < 4 ether) { console.log("ALERT: deposit too low on chain ID %s! Deposit: %s\n", block.chainid, paymasterDeposit); } From 9c439f2b9c557693e6a18f1094cf9b1fe9209928 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 13 Nov 2023 18:30:30 +0100 Subject: [PATCH 08/83] huge refactor sessionKeys and ERC6551 --- contracts/core/BaseOpenfortAccount.sol | 59 +- .../core/eip6551/EIP6551OpenfortAccount.sol | 92 +- .../UpgradeableOpenfortAccount.sol | 2 +- remappings.txt | 1 + script/UserOpTestCounter.sol | 2 +- .../eip6551/EIP6551OpenfortAccountTest.t.sol | 78 +- .../eip6551/EIP6551OpenfortBenchmark.t.sol | 1174 ++++++++--------- .../managed/ManagedOpenfortAccountTest.t.sol | 145 +- .../RecoverableOpenfortAccountTest.t.sol | 141 +- .../UpgradeableOpenfortAccountTest.t.sol | 141 +- .../paymaster/OpenfortPaymasterV2Test.t.sol | 28 +- 11 files changed, 943 insertions(+), 920 deletions(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index f8a9012..4cfabe2 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -285,52 +285,6 @@ abstract contract BaseOpenfortAccount is return SIG_VALIDATION_FAILED; } - /** - * Register a master session key to the account - * @param _key session key to register - * @param _validAfter - this session key is valid only after this timestamp. - * @param _validUntil - this session key is valid only up to this timestamp. - * @notice using this function will automatically set the sessionkey as a - * master session key because no further restriction was set. - * @notice default limit set to 100. - */ - function registerSessionKey(address _key, uint48 _validAfter, uint48 _validUntil) public { - registerSessionKey(_key, _validAfter, _validUntil, DEFAULT_LIMIT); - sessionKeys[_key].masterSessionKey = true; - } - - /** - * Register a master session key to the account - * @param _key session key to register - * @param _validAfter - this session key is valid only after this timestamp. - * @param _validUntil - this session key is valid only up to this timestamp. - * @param _limit - limit of uses remaining. - * @notice using this function will automatically set the sessionkey as a - * master session key because no further restriction was set. - */ - function registerSessionKey(address _key, uint48 _validAfter, uint48 _validUntil, uint48 _limit) public { - _requireFromEntryPointOrOwnerorSelf(); - sessionKeys[_key].validAfter = _validAfter; - sessionKeys[_key].validUntil = _validUntil; - sessionKeys[_key].limit = _limit; - sessionKeys[_key].masterSessionKey = false; - sessionKeys[_key].whitelising = false; - emit SessionKeyRegistered(_key); - } - - /** - * Register a session key to the account - * @param _key session key to register - * @param _validAfter - this session key is valid only after this timestamp. - * @param _validUntil - this session key is valid only up to this timestamp. - * @param _whitelist - this session key can only interact with the addresses in the _whitelist. - */ - function registerSessionKey(address _key, uint48 _validAfter, uint48 _validUntil, address[] calldata _whitelist) - public - { - registerSessionKey(_key, _validAfter, _validUntil, DEFAULT_LIMIT, _whitelist); - } - /** * Register a session key to the account * @param _key session key to register @@ -350,18 +304,24 @@ abstract contract BaseOpenfortAccount is // Not sure why changing this for a custom error increases gas dramatically require(_whitelist.length < 11, "Whitelist too big"); - for (uint256 i = 0; i < _whitelist.length;) { + uint256 i = 0; + for (i; i < _whitelist.length;) { sessionKeys[_key].whitelist[_whitelist[i]] = true; unchecked { ++i; // gas optimization } } + if (i > 0) { + sessionKeys[_key].whitelising = true; + sessionKeys[_key].masterSessionKey = false; + } else { + if (_limit == ((2 ** 48) - 1)) sessionKeys[_key].masterSessionKey = true; + else sessionKeys[_key].masterSessionKey = false; + } sessionKeys[_key].validAfter = _validAfter; sessionKeys[_key].validUntil = _validUntil; sessionKeys[_key].limit = _limit; - sessionKeys[_key].masterSessionKey = false; - sessionKeys[_key].whitelising = true; emit SessionKeyRegistered(_key); } @@ -374,6 +334,7 @@ abstract contract BaseOpenfortAccount is _requireFromEntryPointOrOwnerorSelf(); if (sessionKeys[_key].validUntil != 0) { sessionKeys[_key].validUntil = 0; + sessionKeys[_key].masterSessionKey = false; emit SessionKeyRevoked(_key); } } diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index 512bcd4..9dd3082 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -8,8 +8,8 @@ import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interface import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; -import "./interfaces/IERC6551Account.sol"; -import "./utils/Bytecode.sol"; +import "erc6551/src/interfaces/IERC6551Account.sol"; +import {ERC6551AccountLib} from "erc6551/src/lib/ERC6551AccountLib.sol"; import {BaseAccount, UserOperation, IEntryPoint} from "account-abstraction/core/BaseAccount.sol"; import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; import "account-abstraction/core/Helpers.sol" as Helpers; @@ -45,6 +45,8 @@ contract EIP6551OpenfortAccount is bytes4 internal constant EXECUTEBATCH_SELECTOR = 0x47e1da2a; uint48 internal constant DEFAULT_LIMIT = 100; + uint256 public state; + /** * Struct like ValidationData (from the EIP-4337) - alpha solution - to keep track of session keys' data * @param validAfter this sessionKey is valid only after this timestamp. @@ -93,6 +95,7 @@ contract EIP6551OpenfortAccount is emit EntryPointUpdated(entrypointContract, _entrypoint); entrypointContract = _entrypoint; __EIP712_init("Openfort", "0.4"); + state = 1; } /** @@ -122,11 +125,10 @@ contract EIP6551OpenfortAccount is } } - function owner() public view returns (address) { - (uint256 chainId, address tokenContract, uint256 tokenId) = this.token(); + function owner() public view virtual returns (address) { + (uint256 chainId, address contractAddress, uint256 tokenId) = token(); if (chainId != block.chainid) return address(0); - - return IERC721(tokenContract).ownerOf(tokenId); + return IERC721(contractAddress).ownerOf(tokenId); } /** @@ -147,11 +149,14 @@ contract EIP6551OpenfortAccount is revert(add(result, 32), mload(result)) } } + ++state; } - function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId) { - uint256 length = address(this).code.length; - return abi.decode(Bytecode.codeAt(address(this), length - 0x60, length), (uint256, address, uint256)); + /** + * @dev {See IERC6551Account-token} + */ + function token() public view virtual override returns (uint256, address, uint256) { + return ERC6551AccountLib.token(); } /** @@ -272,6 +277,7 @@ contract EIP6551OpenfortAccount is function execute(address dest, uint256 value, bytes calldata func) external { _requireFromEntryPointOrOwner(); _call(dest, value, func); + ++state; } /** @@ -287,6 +293,7 @@ contract EIP6551OpenfortAccount is unchecked { ++i; // gas optimization } + ++state; } } @@ -305,6 +312,7 @@ contract EIP6551OpenfortAccount is */ function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public { _requireFromOwner(); + ++state; entryPoint().withdrawTo(withdrawAddress, amount); } @@ -318,6 +326,7 @@ contract EIP6551OpenfortAccount is revert(add(result, 32), mload(result)) } } + ++state; } /** @@ -344,50 +353,13 @@ contract EIP6551OpenfortAccount is return SIG_VALIDATION_FAILED; } - /** - * Register a master session key to the account - * @param _key session key to register - * @param _validAfter - this session key is valid only after this timestamp. - * @param _validUntil - this session key is valid only up to this timestamp. - * @notice using this function will automatically set the sessionkey as a - * master session key because no further restriction was set. - * @notice default limit set to 100. - */ - function registerSessionKey(address _key, uint48 _validAfter, uint48 _validUntil) public { - registerSessionKey(_key, _validAfter, _validUntil, DEFAULT_LIMIT); - sessionKeys[_key].masterSessionKey = true; - } - - /** - * Register a master session key to the account - * @param _key session key to register - * @param _validAfter - this session key is valid only after this timestamp. - * @param _validUntil - this session key is valid only up to this timestamp. - * @param _limit - limit of uses remaining. - * @notice using this function will automatically set the sessionkey as a - * master session key because no further restriction was set. - */ - function registerSessionKey(address _key, uint48 _validAfter, uint48 _validUntil, uint48 _limit) public { - _requireFromEntryPointOrOwnerorSelf(); - sessionKeys[_key].validAfter = _validAfter; - sessionKeys[_key].validUntil = _validUntil; - sessionKeys[_key].limit = _limit; - sessionKeys[_key].masterSessionKey = false; - sessionKeys[_key].whitelising = false; - emit SessionKeyRegistered(_key); + function isValidSigner(address signer, bytes calldata) external view virtual returns (bytes4) { + if (_isValidSigner(signer)) return IERC6551Account.isValidSigner.selector; + return bytes4(0); } - /** - * Register a session key to the account - * @param _key session key to register - * @param _validAfter - this session key is valid only after this timestamp. - * @param _validUntil - this session key is valid only up to this timestamp. - * @param _whitelist - this session key can only interact with the addresses in the _whitelist. - */ - function registerSessionKey(address _key, uint48 _validAfter, uint48 _validUntil, address[] calldata _whitelist) - public - { - registerSessionKey(_key, _validAfter, _validUntil, DEFAULT_LIMIT, _whitelist); + function _isValidSigner(address signer) internal view virtual returns (bool) { + return signer == owner(); } /** @@ -409,19 +381,21 @@ contract EIP6551OpenfortAccount is // Not sure why changing this for a custom error increases gas dramatically require(_whitelist.length < 11, "Whitelist too big"); - for (uint256 i = 0; i < _whitelist.length;) { + uint256 i = 0; + for (i; i < _whitelist.length;) { sessionKeys[_key].whitelist[_whitelist[i]] = true; unchecked { ++i; // gas optimization } } + if (i != 0) sessionKeys[_key].whitelising = true; + else if (_limit == 2 ** 48 - 1) sessionKeys[_key].masterSessionKey = true; sessionKeys[_key].validAfter = _validAfter; sessionKeys[_key].validUntil = _validUntil; sessionKeys[_key].limit = _limit; sessionKeys[_key].masterSessionKey = false; - sessionKeys[_key].whitelising = true; - + ++state; emit SessionKeyRegistered(_key); } @@ -431,8 +405,10 @@ contract EIP6551OpenfortAccount is */ function revokeSessionKey(address _key) external { _requireFromEntryPointOrOwnerorSelf(); + ++state; if (sessionKeys[_key].validUntil != 0) { sessionKeys[_key].validUntil = 0; + sessionKeys[_key].masterSessionKey = false; emit SessionKeyRevoked(_key); } } @@ -456,14 +432,8 @@ contract EIP6551OpenfortAccount is revert ZeroAddressNotAllowed(); } _requireFromOwner(); + ++state; emit EntryPointUpdated(entrypointContract, _newEntrypoint); entrypointContract = _newEntrypoint; } - - /** - * Like getNonce() from EIP4337. Needed to comply with EIP6551. - */ - function nonce() external view returns (uint256) { - return getNonce(); - } } diff --git a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol index 3ffb1d2..5539661 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol @@ -27,7 +27,7 @@ contract UpgradeableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradeable { emit EntryPointUpdated(entrypointContract, _entrypoint); _transferOwnership(_defaultAdmin); entrypointContract = _entrypoint; - __EIP712_init("Openfort", "0.4"); + __EIP712_init("Openfort", "0.5"); } function _authorizeUpgrade(address) internal override onlyOwner {} diff --git a/remappings.txt b/remappings.txt index d796bd0..a2670ba 100644 --- a/remappings.txt +++ b/remappings.txt @@ -4,3 +4,4 @@ ds-test/=lib/forge-std/lib/ds-test/src/ eth-gas-reporter/=node_modules/eth-gas-reporter/ forge-std/=lib/forge-std/src/ hardhat/=node_modules/hardhat/ +erc6551/=lib/erc6551/ diff --git a/script/UserOpTestCounter.sol b/script/UserOpTestCounter.sol index 08b8b4e..e08c7c2 100644 --- a/script/UserOpTestCounter.sol +++ b/script/UserOpTestCounter.sol @@ -127,7 +127,7 @@ contract UserOpTestCounter is Script { function run() public { vm.startBroadcast(deployPrivKey); - // Verifiy that the counter is still set to 0 + // Verify that the counter is still set to 0 assert(testCounter.counters(deployAddress) == 0); // Count using deployPrivKey testCounter.count(); diff --git a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol index 8a86466..0ea9de6 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol @@ -6,7 +6,7 @@ import {SigUtils} from "../../utils/SigUtils.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {VIPNFT} from "contracts/mock/VipNFT.sol"; -import {ERC6551Registry, ERC6551AccountCreated} from "lib/erc6551/src/ERC6551Registry.sol"; +import {ERC6551Registry, IERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; contract EIP6551OpenfortAccountTest is Test { @@ -15,9 +15,10 @@ contract EIP6551OpenfortAccountTest is Test { EntryPoint public entryPoint; ERC6551Registry public erc6551Registry; EIP6551OpenfortAccount public eip6551OpenfortAccount; - EIP6551OpenfortAccount implEIP6551OpenfortAccount; + EIP6551OpenfortAccount public implEIP6551OpenfortAccount; + bytes32 public versionSalt = vm.envBytes32("VERSION_SALT"); address public account; - VIPNFT testToken; + VIPNFT public testToken; // Testing addresses address private factoryAdmin; @@ -72,7 +73,7 @@ contract EIP6551OpenfortAccountTest is Test { ops[0] = op; } - /* + /* * Auxiliary function to generate a userOP using the execute() * from the account */ @@ -90,7 +91,7 @@ contract EIP6551OpenfortAccountTest is Test { return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); } - /* + /* * Auxiliary function to generate a userOP using the executeBatch() * from the account */ @@ -143,15 +144,27 @@ contract EIP6551OpenfortAccountTest is Test { entryPoint = EntryPoint(payable(targetAddr)); } + // If we are in a fork + if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { + erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); + } + // If not a fork, deploy entryPoint (at correct address) + else { + ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); + bytes memory code = address(ERC6551Registry_aux).code; + address targetAddr = address(vm.envAddress("ERC6551_REGISTRY_ADDRESS")); + vm.etch(targetAddr, code); + erc6551Registry = ERC6551Registry(payable(targetAddr)); + } + // deploy a new VIPNFT collection testToken = new VIPNFT(); implEIP6551OpenfortAccount = new EIP6551OpenfortAccount(); - erc6551Registry = new ERC6551Registry(); - - address eip6551OpenfortAccountAddress = - erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 1, 1, ""); + address eip6551OpenfortAccountAddress = erc6551Registry.createAccount( + address(implEIP6551OpenfortAccount), versionSalt, chainId, address(testToken), 1 + ); eip6551OpenfortAccount = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress)); eip6551OpenfortAccount.initialize(address(entryPoint)); @@ -163,7 +176,7 @@ contract EIP6551OpenfortAccountTest is Test { /* * Test reinitialize. It should fail. - * + * */ function testFailReinitialize() public { eip6551OpenfortAccount.initialize(address(entryPoint)); @@ -193,8 +206,9 @@ contract EIP6551OpenfortAccountTest is Test { assembly { chainId := chainid() } - address eip6551OpenfortAccountAddress2 = - erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 1, 2, ""); + address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( + address(implEIP6551OpenfortAccount), bytes32(0), chainId, address(testToken), 1 + ); EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); eip6551OpenfortAccount2.initialize(address(entryPoint)); @@ -211,12 +225,7 @@ contract EIP6551OpenfortAccountTest is Test { chainId := chainid() } address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), - chainId, - address(testToken), - 1, - 2, - abi.encodeWithSignature("initialize(address)", address(entryPoint)) + address(implEIP6551OpenfortAccount), versionSalt, chainId, address(testToken), 1 ); EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); @@ -233,12 +242,7 @@ contract EIP6551OpenfortAccountTest is Test { chainId := chainid() } address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), - chainId, - address(testToken), - 1, - 2, - abi.encodeWithSignature("initialize(address)", address(entryPoint)) + address(implEIP6551OpenfortAccount), versionSalt, chainId, address(testToken), 1 ); EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); @@ -300,35 +304,35 @@ contract EIP6551OpenfortAccountTest is Test { // Get the counterfactual address vm.prank(factoryAdmin); address eip6551OpenfortAccountAddress2 = - erc6551Registry.account(address(eip6551OpenfortAccount), chainId, address(testToken), 1, 2); + erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1); // Expect that we will see an event containing the account and admin - vm.expectEmit(true, true, false, true); - emit ERC6551AccountCreated( - eip6551OpenfortAccountAddress2, address(eip6551OpenfortAccount), chainId, address(testToken), 1, 2 - ); + // vm.expectEmit(true, true, false, true); + // emit IERC6551Registry.ERC6551AccountCreated( + // eip6551OpenfortAccountAddress2, address(eip6551OpenfortAccount), chainId, address(testToken), 1, 2 + // ); // Deploy a static account to the counterfactual address vm.prank(factoryAdmin); - erc6551Registry.createAccount(address(eip6551OpenfortAccount), chainId, address(testToken), 1, 2, ""); + erc6551Registry.createAccount(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); assertEq( eip6551OpenfortAccountAddress2, - erc6551Registry.account(address(eip6551OpenfortAccount), chainId, address(testToken), 1, 2) - ); - assertNotEq( - eip6551OpenfortAccountAddress2, - erc6551Registry.account(address(eip6551OpenfortAccount), chainId, address(testToken), 1, 3) + erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1) ); + // assertNotEq( + // eip6551OpenfortAccountAddress2, + // erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1) + // ); assertNotEq( eip6551OpenfortAccountAddress2, - erc6551Registry.account(address(eip6551OpenfortAccount), chainId + 1, address(testToken), 1, 2) + erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId + 1, address(testToken), 1) ); assertNotEq( eip6551OpenfortAccountAddress2, - erc6551Registry.account(address(eip6551OpenfortAccount), chainId, address(0), 1, 2) + erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(0), 1) ); } } diff --git a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol index 78a9d5f..64954bf 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol @@ -14,590 +14,590 @@ import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/Upgradeable import {ERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; -contract EIP6551OpenfortBenchmark is Test { - using ECDSA for bytes32; - - EntryPoint public entryPoint; - - UpgradeableOpenfortAccount public implUpgradeableOpenfortAccount; - UpgradeableOpenfortFactory public upgradeableOpenfortFactory; - UpgradeableOpenfortAccount public upgradeableOpenfortAccount; - - ERC6551Registry public erc6551Registry; - EIP6551OpenfortAccount public eip6551OpenfortAccount; - EIP6551OpenfortAccount implEIP6551OpenfortAccount; - - uint256 public chainId; - - VIPNFT testToken; - USDC testUSDC; - - // Testing addresses - address private factoryAdmin; - uint256 private factoryAdminPKey; - - address private accountAdmin; - uint256 private accountAdminPKey; - - address public upgradeableOpenfortAddressComplex; - UpgradeableOpenfortAccount public upgradeableOpenfortAccountComplex; - - address public eip6551OpenfortAddressComplex; - EIP6551OpenfortAccount public eip6551OpenfortAccountComplex; - - address payable private beneficiary = payable(makeAddr("beneficiary")); - - event AccountCreated( - address account, address implementation, uint256 chainId, address tokenContract, uint256 tokenId, uint256 salt - ); - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (UserOperation[] memory ops) { - uint256 nonce = entryPoint.getNonce(sender, 0); - - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 0, - maxPriorityFeePerGas: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /** - * @notice Initialize the UpgradeableOpenfortAccount testing contract. - * Scenario: - * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory - * - accountAdmin is the account used to deploy new static accounts - * - entryPoint is the singleton EntryPoint - * - testCounter is the counter used to test userOps - */ - function setUp() public { - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - - uint256 auxChainId; - assembly { - auxChainId := chainid() - } - - chainId = auxChainId; - - vm.startPrank(factoryAdmin); - - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } - - // deploy upgradeable account implementation - implUpgradeableOpenfortAccount = new UpgradeableOpenfortAccount(); - // deploy upgradeable account factory - upgradeableOpenfortFactory = new UpgradeableOpenfortFactory( - payable(address(entryPoint)), - address(implUpgradeableOpenfortAccount) - ); - - // Create an upgradeable account wallet and get its address - address upgradeableOpenfortAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); - - upgradeableOpenfortAccount = UpgradeableOpenfortAccount(payable(upgradeableOpenfortAddress)); - - // deploy a new VIPNFT collection - testToken = new VIPNFT(); - - implEIP6551OpenfortAccount = new EIP6551OpenfortAccount(); - - erc6551Registry = new ERC6551Registry(); - - address eip6551OpenfortAccountAddress = - erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 1, 1, ""); - - eip6551OpenfortAccount = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress)); - eip6551OpenfortAccount.initialize(address(entryPoint)); - - testToken.mint(accountAdmin, 1); - - testUSDC = new USDC(); - testUSDC.mint(accountAdmin, 100 ether); - - // Declarations for complex tests - - upgradeableOpenfortAddressComplex = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "complex"); - upgradeableOpenfortAccountComplex = UpgradeableOpenfortAccount(payable(upgradeableOpenfortAddressComplex)); - - testToken.mint(upgradeableOpenfortAddressComplex, 2); - - eip6551OpenfortAddressComplex = - erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 2, 2, ""); - eip6551OpenfortAccountComplex = EIP6551OpenfortAccount(payable(eip6551OpenfortAddressComplex)); - - vm.stopPrank(); - } - - /* - * Create a 2nd Upgradeable account with accountAdmin as the owner - */ - function test1CreateUpgradeableAccount() public { - address upgradeableOpenfortAccountAddress2 = - upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); - UpgradeableOpenfortAccount upgradeableOpenfortAccountAccount2 = - UpgradeableOpenfortAccount(payable(upgradeableOpenfortAccountAddress2)); - IEntryPoint e = upgradeableOpenfortAccountAccount2.entryPoint(); - assertEq(address(e), address(entryPoint)); - } - - /* - * Create a 2nd EIP6551 account with testToken as the owner and initialize later - */ - function test1CreateEIP6551AccountInitAfter() public { - address eip6551OpenfortAccountAddress2 = - erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 1, 2, ""); - - EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); - eip6551OpenfortAccount2.initialize(address(entryPoint)); - IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); - assertEq(address(e), address(entryPoint)); - } - - /* - * Create a 2nd EIP6551 account with testToken as the owner and initialize during creation - */ - function test1CreateEIP6551AccountInitDuringCreation() public { - address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), - chainId, - address(testToken), - 3, - 1, - abi.encodeWithSignature("initialize(address)", address(entryPoint)) - ); - EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); - IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); - assertEq(address(e), address(entryPoint)); - } - - /* - * Test owner() function. - * Check that the owner of the upgradeable account is accountAdmin - */ - function test2OwnerUpgradeable() public { - assertEq(upgradeableOpenfortAccount.owner(), accountAdmin); - } - - /* - * Test owner() function. - * Check that the owner of the eip6551 account is the owner of the NFT - */ - function test2OwnerEIP6551() public { - assertEq(eip6551OpenfortAccount.owner(), testToken.ownerOf(1)); - } - - /* - * Test transferOwnership() function using upgradeable accounts. - */ - function test3TransferOwnerUpgradeable() public { - assertEq(upgradeableOpenfortAccount.owner(), accountAdmin); - - vm.prank(accountAdmin); - upgradeableOpenfortAccount.transferOwnership(factoryAdmin); - - assertEq(upgradeableOpenfortAccount.owner(), accountAdmin); - assertEq(upgradeableOpenfortAccount.pendingOwner(), factoryAdmin); - - vm.prank(factoryAdmin); - upgradeableOpenfortAccount.acceptOwnership(); - - assertEq(upgradeableOpenfortAccount.owner(), factoryAdmin); - } - - /* - * Test transferOwnership() function using EIP6551 accounts. - */ - function test3TransferOwnerEIP6551() public { - assertEq(eip6551OpenfortAccount.owner(), accountAdmin); - - vm.prank(accountAdmin); - testToken.safeTransferFrom(accountAdmin, factoryAdmin, 1); - - assertEq(eip6551OpenfortAccount.owner(), factoryAdmin); - } - - /* - * Test transferOwnership() function using EIP6551 account with a userOp - * It will fail because the msg.sender doing the transferFrom() is the EntryPoint - * the user should do it with a regular TX or approving the EntryPoint to spend - */ - function testFailTransferOwnerEIP6551UserOp() public { - assertEq(eip6551OpenfortAccount.owner(), accountAdmin); - - address _target = address(testToken); - bytes memory _callData = - abi.encodeWithSignature("transferFrom(address,address,uint256)", accountAdmin, factoryAdmin, 1); - - UserOperation[] memory userOp = _setupUserOpExecute( - address(eip6551OpenfortAccount), - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, 0, _callData) - ); - - entryPoint.depositTo{value: 1000000000000000000}(address(eip6551OpenfortAccount)); - - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - assertEq(eip6551OpenfortAccount.owner(), factoryAdmin); - } - - /* - * Test transfer funds using upgradeable accounts. - */ - function test4TransferFundsUpgradeable() public { - address upgradeableOpenfortAddress = payable(address((upgradeableOpenfortAccount))); - console.log("Admin balance: ", accountAdmin.balance); - console.log("Upgradeable Openfort Account balance: ", upgradeableOpenfortAddress.balance); - - vm.prank(accountAdmin); - (bool ok,) = upgradeableOpenfortAddress.call{value: 50 ether}(""); - assert(ok); - console.log("Admin balance: ", accountAdmin.balance); - console.log("Upgradeable Openfort Account balance: ", upgradeableOpenfortAddress.balance); - - vm.prank(accountAdmin); - upgradeableOpenfortAccount.execute(accountAdmin, 40 ether, ""); - - console.log("Admin balance: ", accountAdmin.balance); - console.log("Upgradeable Openfort Account balance: ", upgradeableOpenfortAddress.balance); - } - - /* - * Test transfer funds function using EIP6551 accounts. - */ - function test4TransferFundsEIP6551() public { - address eip6551OpenfortAddress = payable(address((eip6551OpenfortAccount))); - console.log("Admin balance: ", accountAdmin.balance); - console.log("EIP6551 Openfort Account balance: ", eip6551OpenfortAddress.balance); - - vm.prank(accountAdmin); - (bool ok,) = eip6551OpenfortAddress.call{value: 50 ether}(""); - assert(ok); - console.log("Admin balance: ", accountAdmin.balance); - console.log("EIP6551 Openfort Account balance: ", eip6551OpenfortAddress.balance); - - vm.prank(accountAdmin); - eip6551OpenfortAccount.execute(accountAdmin, 40 ether, ""); - - console.log("Admin balance: ", accountAdmin.balance); - console.log("EIP6551 Openfort Account balance: ", eip6551OpenfortAddress.balance); - } - - /* - * Test transfer ERC20 using upgradeable accounts. - */ - function test5TransferERC20Upgradeable() public { - address upgradeableOpenfortAddress = payable(address((upgradeableOpenfortAccount))); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - - vm.prank(accountAdmin); - testUSDC.transfer(upgradeableOpenfortAddress, 50 ether); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - - vm.prank(accountAdmin); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 50 ether) - ); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - } - - /* - * Test transfer ERC20 function using EIP6551 accounts. - */ - function test5TransferERC20EIP6551() public { - address eip6551OpenfortAddress = payable(address((eip6551OpenfortAccount))); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); - - vm.prank(accountAdmin); - testUSDC.transfer(eip6551OpenfortAddress, 50 ether); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); - - vm.prank(accountAdmin); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 50 ether) - ); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); - } - - /* - * Test multiple transfers ERC20 using upgradeable accounts. - */ - function test6TransferMultipleERC20Upgradeable() public { - address upgradeableOpenfortAddress = payable(address((upgradeableOpenfortAccount))); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - - vm.prank(accountAdmin); - testUSDC.transfer(upgradeableOpenfortAddress, 50 ether); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - - vm.startPrank(accountAdmin); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - upgradeableOpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - vm.stopPrank(); - - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - } - - /* - * Test multiple transfers ERC20 function using EIP6551 accounts. - */ - function test6TransferMultipleERC20EIP6551() public { - address eip6551OpenfortAddress = payable(address((eip6551OpenfortAccount))); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); - - vm.prank(accountAdmin); - testUSDC.transfer(eip6551OpenfortAddress, 50 ether); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); - - vm.startPrank(accountAdmin); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - eip6551OpenfortAccount.execute( - address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) - ); - vm.stopPrank(); - console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); - console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); - } - - /* - * Test owner() function. - * Check that the owner of the eip6551 account is the owner of the NFT - */ - function test7ComplexOwner() public { - assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); - } - - /* - * Test transferOwnership() function using upgradeable account that have EIP6551 accounts. - * Scenario: a complex account changes the ownership; all NFTs are manageable by the new owner - */ - function test8TransferOwner4337Complex() public { - // The EOA is the owner of the Upgradeable account - assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); - - // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT - assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); - assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); - - vm.prank(accountAdmin); - upgradeableOpenfortAccountComplex.transferOwnership(factoryAdmin); - - assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); - assertEq(upgradeableOpenfortAccountComplex.pendingOwner(), factoryAdmin); - - vm.prank(factoryAdmin); - upgradeableOpenfortAccountComplex.acceptOwnership(); - - assertEq(upgradeableOpenfortAccountComplex.owner(), factoryAdmin); - assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); - assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); - } - - /* - * Test transferOwnership() function using upgradeable account that have EIP6551 accounts. - * Scenario: a complex account transfer an NFT to send an EIP6551 account to another user - */ - function test9TransferOwnerEIP6551Complex() public { - // The EOA is the owner of the Upgradeable account - assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); - // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT - assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); - assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); - - vm.prank(accountAdmin); - upgradeableOpenfortAccountComplex.execute( - address(testToken), - 0, - abi.encodeWithSignature( - "transferFrom(address,address,uint256)", upgradeableOpenfortAddressComplex, factoryAdmin, 2 - ) - ); - assertEq(testToken.ownerOf(2), factoryAdmin); - assertEq(eip6551OpenfortAccountComplex.owner(), factoryAdmin); - } - - /* - * Test transferOwnership() function using upgradeable account that have EIP6551 accounts. - */ - function test9TransferOwnerEIP6551ComplexUserOp() public { - // The EOA is the owner of the Upgradeable account - assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); - // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT - assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); - assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); - - UserOperation[] memory userOp = _setupUserOpExecute( - upgradeableOpenfortAddressComplex, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature( - "safeTransferFrom(address,address,uint256)", upgradeableOpenfortAddressComplex, factoryAdmin, 2 - ) - ); - - entryPoint.depositTo{value: 1000000000000000000}(upgradeableOpenfortAddressComplex); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - assertEq(testToken.ownerOf(2), factoryAdmin); - assertEq(eip6551OpenfortAccountComplex.owner(), factoryAdmin); - } -} +// contract EIP6551OpenfortBenchmark is Test { +// using ECDSA for bytes32; + +// EntryPoint public entryPoint; + +// UpgradeableOpenfortAccount public implUpgradeableOpenfortAccount; +// UpgradeableOpenfortFactory public upgradeableOpenfortFactory; +// UpgradeableOpenfortAccount public upgradeableOpenfortAccount; + +// ERC6551Registry public erc6551Registry; +// EIP6551OpenfortAccount public eip6551OpenfortAccount; +// EIP6551OpenfortAccount implEIP6551OpenfortAccount; + +// uint256 public chainId; + +// VIPNFT testToken; +// USDC testUSDC; + +// // Testing addresses +// address private factoryAdmin; +// uint256 private factoryAdminPKey; + +// address private accountAdmin; +// uint256 private accountAdminPKey; + +// address public upgradeableOpenfortAddressComplex; +// UpgradeableOpenfortAccount public upgradeableOpenfortAccountComplex; + +// address public eip6551OpenfortAddressComplex; +// EIP6551OpenfortAccount public eip6551OpenfortAccountComplex; + +// address payable private beneficiary = payable(makeAddr("beneficiary")); + +// event AccountCreated( +// address account, address implementation, uint256 chainId, address tokenContract, uint256 tokenId, uint256 salt +// ); + +// /* +// * Auxiliary function to generate a userOP +// */ +// function _setupUserOp( +// address sender, +// uint256 _signerPKey, +// bytes memory _initCode, +// bytes memory _callDataForEntrypoint +// ) internal returns (UserOperation[] memory ops) { +// uint256 nonce = entryPoint.getNonce(sender, 0); + +// // Get user op fields +// UserOperation memory op = UserOperation({ +// sender: sender, +// nonce: nonce, +// initCode: _initCode, +// callData: _callDataForEntrypoint, +// callGasLimit: 500_000, +// verificationGasLimit: 500_000, +// preVerificationGas: 500_000, +// maxFeePerGas: 0, +// maxPriorityFeePerGas: 0, +// paymasterAndData: bytes(""), +// signature: bytes("") +// }); + +// // Sign UserOp +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); +// bytes memory userOpSignature = abi.encodePacked(r, s, v); + +// address recoveredSigner = ECDSA.recover(msgHash, v, r, s); +// address expectedSigner = vm.addr(_signerPKey); +// assertEq(recoveredSigner, expectedSigner); + +// op.signature = userOpSignature; + +// // Store UserOp +// ops = new UserOperation[](1); +// ops[0] = op; +// } + +// /* +// * Auxiliary function to generate a userOP using the execute() +// * from the account +// */ +// function _setupUserOpExecute( +// address sender, +// uint256 _signerPKey, +// bytes memory _initCode, +// address _target, +// uint256 _value, +// bytes memory _callData +// ) internal returns (UserOperation[] memory) { +// bytes memory callDataForEntrypoint = +// abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); + +// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); +// } + +// /* +// * Auxiliary function to generate a userOP using the executeBatch() +// * from the account +// */ +// function _setupUserOpExecuteBatch( +// address sender, +// uint256 _signerPKey, +// bytes memory _initCode, +// address[] memory _target, +// uint256[] memory _value, +// bytes[] memory _callData +// ) internal returns (UserOperation[] memory) { +// bytes memory callDataForEntrypoint = +// abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); + +// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); +// } + +// /** +// * @notice Initialize the UpgradeableOpenfortAccount testing contract. +// * Scenario: +// * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory +// * - accountAdmin is the account used to deploy new static accounts +// * - entryPoint is the singleton EntryPoint +// * - testCounter is the counter used to test userOps +// */ +// function setUp() public { +// // Setup and fund signers +// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); +// vm.deal(factoryAdmin, 100 ether); +// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); +// vm.deal(accountAdmin, 100 ether); + +// uint256 auxChainId; +// assembly { +// auxChainId := chainid() +// } + +// chainId = auxChainId; + +// vm.startPrank(factoryAdmin); + +// // If we are in a fork +// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { +// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); +// } +// // If not a fork, deploy entryPoint (at correct address) +// else { +// EntryPoint entryPoint_aux = new EntryPoint(); +// bytes memory code = address(entryPoint_aux).code; +// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); +// vm.etch(targetAddr, code); +// entryPoint = EntryPoint(payable(targetAddr)); +// } + +// // deploy upgradeable account implementation +// implUpgradeableOpenfortAccount = new UpgradeableOpenfortAccount(); +// // deploy upgradeable account factory +// upgradeableOpenfortFactory = new UpgradeableOpenfortFactory( +// payable(address(entryPoint)), +// address(implUpgradeableOpenfortAccount) +// ); + +// // Create an upgradeable account wallet and get its address +// address upgradeableOpenfortAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); + +// upgradeableOpenfortAccount = UpgradeableOpenfortAccount(payable(upgradeableOpenfortAddress)); + +// // deploy a new VIPNFT collection +// testToken = new VIPNFT(); + +// implEIP6551OpenfortAccount = new EIP6551OpenfortAccount(); + +// erc6551Registry = new ERC6551Registry(); + +// address eip6551OpenfortAccountAddress = +// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 1, 1, ""); + +// eip6551OpenfortAccount = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress)); +// eip6551OpenfortAccount.initialize(address(entryPoint)); + +// testToken.mint(accountAdmin, 1); + +// testUSDC = new USDC(); +// testUSDC.mint(accountAdmin, 100 ether); + +// // Declarations for complex tests + +// upgradeableOpenfortAddressComplex = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "complex"); +// upgradeableOpenfortAccountComplex = UpgradeableOpenfortAccount(payable(upgradeableOpenfortAddressComplex)); + +// testToken.mint(upgradeableOpenfortAddressComplex, 2); + +// eip6551OpenfortAddressComplex = +// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 2, 2, ""); +// eip6551OpenfortAccountComplex = EIP6551OpenfortAccount(payable(eip6551OpenfortAddressComplex)); + +// vm.stopPrank(); +// } + +// /* +// * Create a 2nd Upgradeable account with accountAdmin as the owner +// */ +// function test1CreateUpgradeableAccount() public { +// address upgradeableOpenfortAccountAddress2 = +// upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); +// UpgradeableOpenfortAccount upgradeableOpenfortAccountAccount2 = +// UpgradeableOpenfortAccount(payable(upgradeableOpenfortAccountAddress2)); +// IEntryPoint e = upgradeableOpenfortAccountAccount2.entryPoint(); +// assertEq(address(e), address(entryPoint)); +// } + +// /* +// * Create a 2nd EIP6551 account with testToken as the owner and initialize later +// */ +// function test1CreateEIP6551AccountInitAfter() public { +// address eip6551OpenfortAccountAddress2 = +// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 1, 2, ""); + +// EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); +// eip6551OpenfortAccount2.initialize(address(entryPoint)); +// IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); +// assertEq(address(e), address(entryPoint)); +// } + +// /* +// * Create a 2nd EIP6551 account with testToken as the owner and initialize during creation +// */ +// function test1CreateEIP6551AccountInitDuringCreation() public { +// address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( +// address(implEIP6551OpenfortAccount), +// chainId, +// address(testToken), +// 3, +// 1, +// abi.encodeWithSignature("initialize(address)", address(entryPoint)) +// ); +// EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); +// IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); +// assertEq(address(e), address(entryPoint)); +// } + +// /* +// * Test owner() function. +// * Check that the owner of the upgradeable account is accountAdmin +// */ +// function test2OwnerUpgradeable() public { +// assertEq(upgradeableOpenfortAccount.owner(), accountAdmin); +// } + +// /* +// * Test owner() function. +// * Check that the owner of the eip6551 account is the owner of the NFT +// */ +// function test2OwnerEIP6551() public { +// assertEq(eip6551OpenfortAccount.owner(), testToken.ownerOf(1)); +// } + +// /* +// * Test transferOwnership() function using upgradeable accounts. +// */ +// function test3TransferOwnerUpgradeable() public { +// assertEq(upgradeableOpenfortAccount.owner(), accountAdmin); + +// vm.prank(accountAdmin); +// upgradeableOpenfortAccount.transferOwnership(factoryAdmin); + +// assertEq(upgradeableOpenfortAccount.owner(), accountAdmin); +// assertEq(upgradeableOpenfortAccount.pendingOwner(), factoryAdmin); + +// vm.prank(factoryAdmin); +// upgradeableOpenfortAccount.acceptOwnership(); + +// assertEq(upgradeableOpenfortAccount.owner(), factoryAdmin); +// } + +// /* +// * Test transferOwnership() function using EIP6551 accounts. +// */ +// function test3TransferOwnerEIP6551() public { +// assertEq(eip6551OpenfortAccount.owner(), accountAdmin); + +// vm.prank(accountAdmin); +// testToken.safeTransferFrom(accountAdmin, factoryAdmin, 1); + +// assertEq(eip6551OpenfortAccount.owner(), factoryAdmin); +// } + +// /* +// * Test transferOwnership() function using EIP6551 account with a userOp +// * It will fail because the msg.sender doing the transferFrom() is the EntryPoint +// * the user should do it with a regular TX or approving the EntryPoint to spend +// */ +// function testFailTransferOwnerEIP6551UserOp() public { +// assertEq(eip6551OpenfortAccount.owner(), accountAdmin); + +// address _target = address(testToken); +// bytes memory _callData = +// abi.encodeWithSignature("transferFrom(address,address,uint256)", accountAdmin, factoryAdmin, 1); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// address(eip6551OpenfortAccount), +// accountAdminPKey, +// bytes(""), +// address(testToken), +// 0, +// abi.encodeWithSignature("execute(address,uint256,bytes)", _target, 0, _callData) +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(address(eip6551OpenfortAccount)); + +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// assertEq(eip6551OpenfortAccount.owner(), factoryAdmin); +// } + +// /* +// * Test transfer funds using upgradeable accounts. +// */ +// function test4TransferFundsUpgradeable() public { +// address upgradeableOpenfortAddress = payable(address((upgradeableOpenfortAccount))); +// console.log("Admin balance: ", accountAdmin.balance); +// console.log("Upgradeable Openfort Account balance: ", upgradeableOpenfortAddress.balance); + +// vm.prank(accountAdmin); +// (bool ok,) = upgradeableOpenfortAddress.call{value: 50 ether}(""); +// assert(ok); +// console.log("Admin balance: ", accountAdmin.balance); +// console.log("Upgradeable Openfort Account balance: ", upgradeableOpenfortAddress.balance); + +// vm.prank(accountAdmin); +// upgradeableOpenfortAccount.execute(accountAdmin, 40 ether, ""); + +// console.log("Admin balance: ", accountAdmin.balance); +// console.log("Upgradeable Openfort Account balance: ", upgradeableOpenfortAddress.balance); +// } + +// /* +// * Test transfer funds function using EIP6551 accounts. +// */ +// function test4TransferFundsEIP6551() public { +// address eip6551OpenfortAddress = payable(address((eip6551OpenfortAccount))); +// console.log("Admin balance: ", accountAdmin.balance); +// console.log("EIP6551 Openfort Account balance: ", eip6551OpenfortAddress.balance); + +// vm.prank(accountAdmin); +// (bool ok,) = eip6551OpenfortAddress.call{value: 50 ether}(""); +// assert(ok); +// console.log("Admin balance: ", accountAdmin.balance); +// console.log("EIP6551 Openfort Account balance: ", eip6551OpenfortAddress.balance); + +// vm.prank(accountAdmin); +// eip6551OpenfortAccount.execute(accountAdmin, 40 ether, ""); + +// console.log("Admin balance: ", accountAdmin.balance); +// console.log("EIP6551 Openfort Account balance: ", eip6551OpenfortAddress.balance); +// } + +// /* +// * Test transfer ERC20 using upgradeable accounts. +// */ +// function test5TransferERC20Upgradeable() public { +// address upgradeableOpenfortAddress = payable(address((upgradeableOpenfortAccount))); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); + +// vm.prank(accountAdmin); +// testUSDC.transfer(upgradeableOpenfortAddress, 50 ether); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); + +// vm.prank(accountAdmin); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 50 ether) +// ); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); +// } + +// /* +// * Test transfer ERC20 function using EIP6551 accounts. +// */ +// function test5TransferERC20EIP6551() public { +// address eip6551OpenfortAddress = payable(address((eip6551OpenfortAccount))); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); + +// vm.prank(accountAdmin); +// testUSDC.transfer(eip6551OpenfortAddress, 50 ether); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); + +// vm.prank(accountAdmin); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 50 ether) +// ); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); +// } + +// /* +// * Test multiple transfers ERC20 using upgradeable accounts. +// */ +// function test6TransferMultipleERC20Upgradeable() public { +// address upgradeableOpenfortAddress = payable(address((upgradeableOpenfortAccount))); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); + +// vm.prank(accountAdmin); +// testUSDC.transfer(upgradeableOpenfortAddress, 50 ether); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); + +// vm.startPrank(accountAdmin); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// upgradeableOpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// vm.stopPrank(); + +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); +// } + +// /* +// * Test multiple transfers ERC20 function using EIP6551 accounts. +// */ +// function test6TransferMultipleERC20EIP6551() public { +// address eip6551OpenfortAddress = payable(address((eip6551OpenfortAccount))); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); + +// vm.prank(accountAdmin); +// testUSDC.transfer(eip6551OpenfortAddress, 50 ether); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); + +// vm.startPrank(accountAdmin); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// eip6551OpenfortAccount.execute( +// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) +// ); +// vm.stopPrank(); +// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); +// console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); +// } + +// /* +// * Test owner() function. +// * Check that the owner of the eip6551 account is the owner of the NFT +// */ +// function test7ComplexOwner() public { +// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// } + +// /* +// * Test transferOwnership() function using upgradeable account that have EIP6551 accounts. +// * Scenario: a complex account changes the ownership; all NFTs are manageable by the new owner +// */ +// function test8TransferOwner4337Complex() public { +// // The EOA is the owner of the Upgradeable account +// assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); + +// // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT +// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); + +// vm.prank(accountAdmin); +// upgradeableOpenfortAccountComplex.transferOwnership(factoryAdmin); + +// assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); +// assertEq(upgradeableOpenfortAccountComplex.pendingOwner(), factoryAdmin); + +// vm.prank(factoryAdmin); +// upgradeableOpenfortAccountComplex.acceptOwnership(); + +// assertEq(upgradeableOpenfortAccountComplex.owner(), factoryAdmin); +// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); +// } + +// /* +// * Test transferOwnership() function using upgradeable account that have EIP6551 accounts. +// * Scenario: a complex account transfer an NFT to send an EIP6551 account to another user +// */ +// function test9TransferOwnerEIP6551Complex() public { +// // The EOA is the owner of the Upgradeable account +// assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); +// // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT +// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); + +// vm.prank(accountAdmin); +// upgradeableOpenfortAccountComplex.execute( +// address(testToken), +// 0, +// abi.encodeWithSignature( +// "transferFrom(address,address,uint256)", upgradeableOpenfortAddressComplex, factoryAdmin, 2 +// ) +// ); +// assertEq(testToken.ownerOf(2), factoryAdmin); +// assertEq(eip6551OpenfortAccountComplex.owner(), factoryAdmin); +// } + +// /* +// * Test transferOwnership() function using upgradeable account that have EIP6551 accounts. +// */ +// function test9TransferOwnerEIP6551ComplexUserOp() public { +// // The EOA is the owner of the Upgradeable account +// assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); +// // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT +// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// upgradeableOpenfortAddressComplex, +// accountAdminPKey, +// bytes(""), +// address(testToken), +// 0, +// abi.encodeWithSignature( +// "safeTransferFrom(address,address,uint256)", upgradeableOpenfortAddressComplex, factoryAdmin, 2 +// ) +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(upgradeableOpenfortAddressComplex); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// assertEq(testToken.ownerOf(2), factoryAdmin); +// assertEq(eip6551OpenfortAccountComplex.owner(), factoryAdmin); +// } +// } diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index f25b6d1..f6ea748 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -221,14 +221,14 @@ contract ManagedOpenfortAccountTest is Test { * Create an account using the factory and make it call count() directly. */ function testIncrementCounterDirect() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); // Make the admin of the static account wallet (deployer) call "count" vm.prank(accountAdmin); ManagedOpenfortAccount(payable(account)).execute(address(testCounter), 0, abi.encodeWithSignature("count()")); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -237,7 +237,7 @@ contract ManagedOpenfortAccountTest is Test { * using the execute() function using the EntryPoint (userOp). Leaveraging ERC-4337. */ function testIncrementCounterViaEntrypoint() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -249,7 +249,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -258,7 +258,7 @@ contract ManagedOpenfortAccountTest is Test { * using the executeBatching() function using the EntryPoint (userOp). Leaveraging ERC-4337. */ function testIncrementCounterViaEntrypointBatching() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); uint256 count = 3; @@ -280,7 +280,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 3); } @@ -288,7 +288,7 @@ contract ManagedOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that is not registered. */ function testFailIncrementCounterViaSessionKeyNotregistered() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -304,7 +304,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -312,15 +312,16 @@ contract ManagedOpenfortAccountTest is Test { * Use a sessionKey that is registered. */ function testIncrementCounterViaSessionKey() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1); + ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -331,7 +332,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -340,12 +341,13 @@ contract ManagedOpenfortAccountTest is Test { * using the EntryPoint (userOp). Then use the sessionKey to count */ function testRegisterSessionKeyViaEntrypoint() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOpExecute( account, @@ -353,7 +355,14 @@ contract ManagedOpenfortAccountTest is Test { bytes(""), account, 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1) + abi.encodeWithSignature( + "registerSessionKey(address,uint48,uint48,uint48,address[])", + sessionKey, + 0, + 2 ** 48 - 1, + 100, + emptyWhitelist + ) ); entryPoint.depositTo{value: 1000000000000000000}(account); @@ -361,7 +370,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); userOp = _setupUserOpExecute( @@ -373,7 +382,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -382,12 +391,13 @@ contract ManagedOpenfortAccountTest is Test { * using the EntryPoint (userOp). Then use that sessionKey to register a second one */ function testRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOpExecute( account, @@ -395,7 +405,14 @@ contract ManagedOpenfortAccountTest is Test { bytes(""), account, 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1) + abi.encodeWithSignature( + "registerSessionKey(address,uint48,uint48,uint48,address[])", + sessionKey, + 0, + 2 ** 48 - 1, + 2 ** 48 - 1, + emptyWhitelist + ) ); entryPoint.depositTo{value: 1000000000000000000}(account); @@ -403,7 +420,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); userOp = _setupUserOpExecute( @@ -415,7 +432,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); address sessionKeyAttack; @@ -428,7 +445,14 @@ contract ManagedOpenfortAccountTest is Test { bytes(""), account, 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKeyAttack, 0, 2 ** 48 - 1) + abi.encodeWithSignature( + "registerSessionKey(address,uint48,uint48,uint48,address[])", + sessionKeyAttack, + 0, + 2 ** 48 - 1, + 100, + emptyWhitelist + ) ); entryPoint.depositTo{value: 1000000000000000000}(account); @@ -442,7 +466,7 @@ contract ManagedOpenfortAccountTest is Test { * using the EntryPoint (userOp). Then use that sessionKey to register a second one */ function testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -463,7 +487,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); userOp = _setupUserOpExecute( @@ -475,7 +499,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); // Verify that the registered key is not a MasterKey nor has whitelisting @@ -508,16 +532,17 @@ contract ManagedOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that is expired. */ function testIncrementCounterViaSessionKeyExpired() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.warp(100); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99); + ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -529,7 +554,7 @@ contract ManagedOpenfortAccountTest is Test { vm.expectRevert(); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -537,15 +562,16 @@ contract ManagedOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that is revoked. */ function testFailIncrementCounterViaSessionKeyRevoked() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0); + ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); ManagedOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( @@ -557,7 +583,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -565,17 +591,18 @@ contract ManagedOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimit() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1); + ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -595,7 +622,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has only increased by one + // Verify that the counter has only increased by one assertEq(testCounter.counters(account), 1); } @@ -603,17 +630,18 @@ contract ManagedOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2); + ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); uint256 count = 3; address[] memory targets = new address[](count); @@ -634,7 +662,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -642,15 +670,16 @@ contract ManagedOpenfortAccountTest is Test { * Should fail, try to revoke a sessionKey using a non-privileged user */ function testFailRevokeSessionKeyInvalidUser() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0); + ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); vm.prank(beneficiary); ManagedOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); @@ -663,7 +692,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -671,7 +700,7 @@ contract ManagedOpenfortAccountTest is Test { * Use a sessionKey with whitelisting to call Execute(). */ function testIncrementCounterViaSessionKeyWhitelisting() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -692,7 +721,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -700,7 +729,7 @@ contract ManagedOpenfortAccountTest is Test { * Should fail, try to register a sessionKey with a large whitelist. */ function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -720,7 +749,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -728,7 +757,7 @@ contract ManagedOpenfortAccountTest is Test { * Use a sessionKey with whitelisting to call ExecuteBatch(). */ function testIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -766,7 +795,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 3); } @@ -774,7 +803,7 @@ contract ManagedOpenfortAccountTest is Test { * Use a sessionKey with whitelisting to call ExecuteBatch(). */ function testFailIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -812,7 +841,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -820,7 +849,7 @@ contract ManagedOpenfortAccountTest is Test { * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). */ function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -841,7 +870,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -849,7 +878,7 @@ contract ManagedOpenfortAccountTest is Test { * Should fail, try to use a sessionKey with invalid whitelisting to call ExecuteBatch(). */ function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -886,7 +915,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -914,14 +943,14 @@ contract ManagedOpenfortAccountTest is Test { vm.prank(accountAdmin2); ManagedOpenfortAccount(payable(account)).acceptOwnership(); - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); // Make the admin of the static account wallet (deployer) call "count" vm.prank(accountAdmin2); ManagedOpenfortAccount(payable(account)).execute(address(testCounter), 0, abi.encodeWithSignature("count()")); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -938,7 +967,7 @@ contract ManagedOpenfortAccountTest is Test { vm.prank(accountAdmin2); ManagedOpenfortAccount(payable(account)).acceptOwnership(); - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -950,7 +979,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -958,7 +987,7 @@ contract ManagedOpenfortAccountTest is Test { * Test an account with testToken instead of TestCount. */ function testMintTokenAccount() public { - // Verifiy that the totalSupply is stil 0 + // Verify that the totalSupply is stil 0 assertEq(testToken.totalSupply(), 0); // Mint 1 to beneficiary @@ -979,7 +1008,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the totalSupply has increased + // Verify that the totalSupply has increased assertEq(testToken.totalSupply(), 2); } @@ -1019,7 +1048,7 @@ contract ManagedOpenfortAccountTest is Test { * Basic test of simulateValidation() to check that it always reverts. */ function testSimulateValidation() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -1049,7 +1078,7 @@ contract ManagedOpenfortAccountTest is Test { vm.expectRevert(); entryPoint.simulateHandleOp(userOp[0], address(0), ""); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } diff --git a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol index 600ce93..76d637e 100644 --- a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol +++ b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol @@ -293,7 +293,7 @@ contract RecoverableOpenfortAccountTest is Test { * Create an account using the factory and make it call count() directly. */ function testIncrementCounterDirect() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" @@ -302,7 +302,7 @@ contract RecoverableOpenfortAccountTest is Test { address(testCounter), 0, abi.encodeWithSignature("count()") ); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -311,7 +311,7 @@ contract RecoverableOpenfortAccountTest is Test { * using the execute() function using the EntryPoint (userOp). Leaveraging ERC-4337. */ function testIncrementCounterViaEntrypoint() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -323,7 +323,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -332,7 +332,7 @@ contract RecoverableOpenfortAccountTest is Test { * using the executeBatching() function using the EntryPoint (userOp). Leaveraging ERC-4337. */ function testIncrementCounterViaEntrypointBatching() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); uint256 count = 3; @@ -354,7 +354,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 3); } @@ -362,7 +362,7 @@ contract RecoverableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that is not registered. */ function testFailIncrementCounterViaSessionKeyNotregistered() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -378,7 +378,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -386,15 +386,16 @@ contract RecoverableOpenfortAccountTest is Test { * Use a sessionKey that is registered. */ function testIncrementCounterViaSessionKey() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1); + RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -405,7 +406,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -414,12 +415,13 @@ contract RecoverableOpenfortAccountTest is Test { * using the EntryPoint (userOp). Then use the sessionKey to count */ function testRegisterSessionKeyViaEntrypoint() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOpExecute( account, @@ -427,7 +429,14 @@ contract RecoverableOpenfortAccountTest is Test { bytes(""), account, 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1) + abi.encodeWithSignature( + "registerSessionKey(address,uint48,uint48,uint48,address[])", + sessionKey, + 0, + 2 ** 48 - 1, + 100, + emptyWhitelist + ) ); entryPoint.depositTo{value: 1000000000000000000}(account); @@ -435,7 +444,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); userOp = _setupUserOpExecute( @@ -447,7 +456,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -456,12 +465,13 @@ contract RecoverableOpenfortAccountTest is Test { * using the EntryPoint (userOp). Then use that sessionKey to register a second one */ function testRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOpExecute( account, @@ -469,7 +479,14 @@ contract RecoverableOpenfortAccountTest is Test { bytes(""), account, 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1) + abi.encodeWithSignature( + "registerSessionKey(address,uint48,uint48,uint48,address[])", + sessionKey, + 0, + 2 ** 48 - 1, + 2 ** 48 - 1, + emptyWhitelist + ) ); entryPoint.depositTo{value: 1000000000000000000}(account); @@ -477,7 +494,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); userOp = _setupUserOpExecute( @@ -489,7 +506,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); address sessionKeyAttack; @@ -502,7 +519,14 @@ contract RecoverableOpenfortAccountTest is Test { bytes(""), account, 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKeyAttack, 0, 2 ** 48 - 1) + abi.encodeWithSignature( + "registerSessionKey(address,uint48,uint48,uint48,address[])", + sessionKeyAttack, + 0, + 2 ** 48 - 1, + 100, + emptyWhitelist + ) ); entryPoint.depositTo{value: 1000000000000000000}(account); @@ -516,7 +540,7 @@ contract RecoverableOpenfortAccountTest is Test { * using the EntryPoint (userOp). Then use that sessionKey to register a second one */ function testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -537,7 +561,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); userOp = _setupUserOpExecute( @@ -549,7 +573,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); // Verify that the registered key is not a MasterKey nor has whitelisting @@ -582,16 +606,17 @@ contract RecoverableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that is expired. */ function testIncrementCounterViaSessionKeyExpired() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.warp(100); vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99); + RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -603,7 +628,7 @@ contract RecoverableOpenfortAccountTest is Test { vm.expectRevert(); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -611,15 +636,16 @@ contract RecoverableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that is revoked. */ function testFailIncrementCounterViaSessionKeyRevoked() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0); + RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); RecoverableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( @@ -631,7 +657,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -639,17 +665,18 @@ contract RecoverableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimit() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1); + RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -669,7 +696,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has only increased by one + // Verify that the counter has only increased by one assertEq(testCounter.counters(account), 1); } @@ -677,17 +704,18 @@ contract RecoverableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2); + RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); uint256 count = 3; address[] memory targets = new address[](count); @@ -708,7 +736,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -716,15 +744,16 @@ contract RecoverableOpenfortAccountTest is Test { * Should fail, try to revoke a sessionKey using a non-privileged user */ function testFailRevokeSessionKeyInvalidUser() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0); + RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); vm.prank(beneficiary); RecoverableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); @@ -737,7 +766,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -745,7 +774,7 @@ contract RecoverableOpenfortAccountTest is Test { * Use a sessionKey with whitelisting to call Execute(). */ function testIncrementCounterViaSessionKeyWhitelisting() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -766,7 +795,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -774,7 +803,7 @@ contract RecoverableOpenfortAccountTest is Test { * Should fail, try to register a sessionKey with a large whitelist. */ function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -794,7 +823,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -802,7 +831,7 @@ contract RecoverableOpenfortAccountTest is Test { * Use a sessionKey with whitelisting to call ExecuteBatch(). */ function testIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -840,7 +869,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 3); } @@ -848,7 +877,7 @@ contract RecoverableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). */ function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -869,7 +898,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -877,7 +906,7 @@ contract RecoverableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey with invalid whitelisting to call ExecuteBatch(). */ function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -914,7 +943,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -939,7 +968,7 @@ contract RecoverableOpenfortAccountTest is Test { vm.prank(accountAdmin2); RecoverableOpenfortAccount(payable(account)).acceptOwnership(); - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" @@ -948,7 +977,7 @@ contract RecoverableOpenfortAccountTest is Test { address(testCounter), 0, abi.encodeWithSignature("count()") ); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -965,7 +994,7 @@ contract RecoverableOpenfortAccountTest is Test { vm.prank(accountAdmin2); RecoverableOpenfortAccount(payable(account)).acceptOwnership(); - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -977,7 +1006,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -985,7 +1014,7 @@ contract RecoverableOpenfortAccountTest is Test { * Test an account with testToken instead of TestCount. */ function testMintTokenAccount() public { - // Verifiy that the totalSupply is stil 0 + // Verify that the totalSupply is stil 0 assertEq(testToken.totalSupply(), 0); // Mint 1 to beneficiary @@ -1006,7 +1035,7 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the totalSupply has increased + // Verify that the totalSupply has increased assertEq(testToken.totalSupply(), 2); } @@ -1046,7 +1075,7 @@ contract RecoverableOpenfortAccountTest is Test { * Basic test of simulateValidation() to check that it always reverts. */ function testSimulateValidation() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -1058,7 +1087,7 @@ contract RecoverableOpenfortAccountTest is Test { vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index d0d97e8..e898eb3 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -218,7 +218,7 @@ contract UpgradeableOpenfortAccountTest is Test { * Create an account using the factory and make it call count() directly. */ function testIncrementCounterDirect() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" @@ -227,7 +227,7 @@ contract UpgradeableOpenfortAccountTest is Test { address(testCounter), 0, abi.encodeWithSignature("count()") ); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -236,7 +236,7 @@ contract UpgradeableOpenfortAccountTest is Test { * using the execute() function using the EntryPoint (userOp). Leaveraging ERC-4337. */ function testIncrementCounterViaEntrypoint() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -248,7 +248,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -257,7 +257,7 @@ contract UpgradeableOpenfortAccountTest is Test { * using the executeBatching() function using the EntryPoint (userOp). Leaveraging ERC-4337. */ function testIncrementCounterViaEntrypointBatching() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); uint256 count = 3; @@ -279,7 +279,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 3); } @@ -287,7 +287,7 @@ contract UpgradeableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that is not registered. */ function testFailIncrementCounterViaSessionKeyNotregistered() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -303,7 +303,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -311,15 +311,16 @@ contract UpgradeableOpenfortAccountTest is Test { * Use a sessionKey that is registered. */ function testIncrementCounterViaSessionKey() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1); + UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -330,7 +331,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -339,12 +340,13 @@ contract UpgradeableOpenfortAccountTest is Test { * using the EntryPoint (userOp). Then use the sessionKey to count */ function testRegisterSessionKeyViaEntrypoint() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOpExecute( account, @@ -352,7 +354,14 @@ contract UpgradeableOpenfortAccountTest is Test { bytes(""), account, 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1) + abi.encodeWithSignature( + "registerSessionKey(address,uint48,uint48,uint48,address[])", + sessionKey, + 0, + 2 ** 48 - 1, + 100, + emptyWhitelist + ) ); entryPoint.depositTo{value: 1000000000000000000}(account); @@ -360,7 +369,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); userOp = _setupUserOpExecute( @@ -372,7 +381,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -381,12 +390,13 @@ contract UpgradeableOpenfortAccountTest is Test { * using the EntryPoint (userOp). Then use that sessionKey to register a second one */ function testRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOpExecute( account, @@ -394,7 +404,14 @@ contract UpgradeableOpenfortAccountTest is Test { bytes(""), account, 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1) + abi.encodeWithSignature( + "registerSessionKey(address,uint48,uint48,uint48,address[])", + sessionKey, + 0, + 2 ** 48 - 1, + 2 ** 48 - 1, + emptyWhitelist + ) ); entryPoint.depositTo{value: 1000000000000000000}(account); @@ -402,7 +419,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); userOp = _setupUserOpExecute( @@ -414,7 +431,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); address sessionKeyAttack; @@ -427,7 +444,14 @@ contract UpgradeableOpenfortAccountTest is Test { bytes(""), account, 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKeyAttack, 0, 2 ** 48 - 1) + abi.encodeWithSignature( + "registerSessionKey(address,uint48,uint48,uint48,address[])", + sessionKeyAttack, + 0, + 2 ** 48 - 1, + 100, + emptyWhitelist + ) ); entryPoint.depositTo{value: 1000000000000000000}(account); @@ -441,7 +465,7 @@ contract UpgradeableOpenfortAccountTest is Test { * using the EntryPoint (userOp). Then use that sessionKey to register a second one */ function testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -462,7 +486,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); userOp = _setupUserOpExecute( @@ -474,7 +498,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); // Verify that the registered key is not a MasterKey nor has whitelisting @@ -507,16 +531,17 @@ contract UpgradeableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that is expired. */ function testIncrementCounterViaSessionKeyExpired() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.warp(100); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99); + UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -528,7 +553,7 @@ contract UpgradeableOpenfortAccountTest is Test { vm.expectRevert(); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -536,15 +561,16 @@ contract UpgradeableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that is revoked. */ function testFailIncrementCounterViaSessionKeyRevoked() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0); + UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); UpgradeableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( @@ -556,7 +582,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -564,17 +590,18 @@ contract UpgradeableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimit() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1); + UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") @@ -594,7 +621,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has only increased by one + // Verify that the counter has only increased by one assertEq(testCounter.counters(account), 1); } @@ -602,17 +629,18 @@ contract UpgradeableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2); + UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); uint256 count = 3; address[] memory targets = new address[](count); @@ -633,7 +661,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -641,15 +669,16 @@ contract UpgradeableOpenfortAccountTest is Test { * Should fail, try to revoke a sessionKey using a non-privileged user */ function testFailRevokeSessionKeyInvalidUser() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0); + UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); vm.prank(beneficiary); UpgradeableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); @@ -662,7 +691,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -670,7 +699,7 @@ contract UpgradeableOpenfortAccountTest is Test { * Use a sessionKey with whitelisting to call Execute(). */ function testIncrementCounterViaSessionKeyWhitelisting() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -691,7 +720,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -699,7 +728,7 @@ contract UpgradeableOpenfortAccountTest is Test { * Should fail, try to register a sessionKey with a large whitelist. */ function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -719,7 +748,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -727,7 +756,7 @@ contract UpgradeableOpenfortAccountTest is Test { * Use a sessionKey with whitelisting to call ExecuteBatch(). */ function testIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -765,7 +794,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 3); } @@ -773,7 +802,7 @@ contract UpgradeableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). */ function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -794,7 +823,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -802,7 +831,7 @@ contract UpgradeableOpenfortAccountTest is Test { * Should fail, try to use a sessionKey with invalid whitelisting to call ExecuteBatch(). */ function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -839,7 +868,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -867,7 +896,7 @@ contract UpgradeableOpenfortAccountTest is Test { vm.prank(accountAdmin2); UpgradeableOpenfortAccount(payable(account)).acceptOwnership(); - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" @@ -876,7 +905,7 @@ contract UpgradeableOpenfortAccountTest is Test { address(testCounter), 0, abi.encodeWithSignature("count()") ); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -893,7 +922,7 @@ contract UpgradeableOpenfortAccountTest is Test { vm.prank(accountAdmin2); UpgradeableOpenfortAccount(payable(account)).acceptOwnership(); - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -905,7 +934,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the counter has increased + // Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -913,7 +942,7 @@ contract UpgradeableOpenfortAccountTest is Test { * Test an account with testToken instead of TestCount. */ function testMintTokenAccount() public { - // Verifiy that the totalSupply is stil 0 + // Verify that the totalSupply is stil 0 assertEq(testToken.totalSupply(), 0); // Mint 1 to beneficiary @@ -934,7 +963,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); - // Verifiy that the totalSupply has increased + // Verify that the totalSupply has increased assertEq(testToken.totalSupply(), 2); } @@ -974,7 +1003,7 @@ contract UpgradeableOpenfortAccountTest is Test { * Basic test of simulateValidation() to check that it always reverts. */ function testSimulateValidation() public { - // Verifiy that the counter is stil set to 0 + // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -986,7 +1015,7 @@ contract UpgradeableOpenfortAccountTest is Test { vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index 8c4a9bc..1a564a0 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -461,7 +461,7 @@ contract OpenfortPaymasterV2Test is Test { vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -487,7 +487,7 @@ contract OpenfortPaymasterV2Test is Test { vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); - // Verifiy that the counter has not increased + // Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -555,7 +555,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - //Verifiy that the counter has increased + //Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -623,7 +623,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); - //Verifiy that the counter has not increased + //Verify that the counter has not increased assertEq(testCounter.counters(account), 0); } @@ -697,7 +697,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased + // Verify that the balance of the smart account has decreased assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); } @@ -770,7 +770,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased + // Verify that the balance of the smart account has decreased assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); } @@ -844,7 +844,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased + // Verify that the balance of the smart account has decreased assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); } @@ -925,7 +925,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased + // Verify that the balance of the smart account has decreased assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); assertEq(testCounter.counters(account), 1); } @@ -1009,7 +1009,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased + // Verify that the balance of the smart account has decreased assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); assertEq(testCounter.counters(account), 1); } @@ -1093,7 +1093,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased + // Verify that the balance of the smart account has decreased assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); assertEq(testCounter.counters(account), 1); } @@ -1170,7 +1170,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - //Verifiy that the counter has increased + //Verify that the counter has increased assertEq(testCounter.counters(account), 1); } @@ -1418,7 +1418,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased + // Verify that the balance of the smart account has decreased assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); assert(openfortPaymaster.getDeposit() < 100 ether); @@ -1503,7 +1503,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verifiy that the balance of the smart account has decreased + // Verify that the balance of the smart account has decreased assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); assert(openfortPaymaster.getDeposit() < 100 ether); @@ -1684,7 +1684,7 @@ contract OpenfortPaymasterV2Test is Test { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - //Verifiy that the counter has increased + //Verify that the counter has increased assertEq(testCounter.counters(account), 1); assert(openfortPaymaster.getDeposit() < 53 ether); // less than 53 because the total cost have decreased From 1b682d73df66d27e18a872fb4c042e1bcc2482ea Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 14 Nov 2023 12:03:49 +0100 Subject: [PATCH 09/83] Minor optimizations --- contracts/core/BaseOpenfortAccount.sol | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index 4cfabe2..1d643c5 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -39,7 +39,6 @@ abstract contract BaseOpenfortAccount is bytes4 internal constant EXECUTE_SELECTOR = 0xb61d27f6; // bytes4(keccak256("executeBatch(address[],uint256[],bytes[])") bytes4 internal constant EXECUTEBATCH_SELECTOR = 0x47e1da2a; - uint48 internal constant DEFAULT_LIMIT = 100; /** * Struct like ValidationData (from the EIP-4337) - alpha solution - to keep track of session keys' data @@ -108,19 +107,18 @@ abstract contract BaseOpenfortAccount is */ function isValidSessionKey(address _sessionKey, bytes calldata callData) public returns (bool) { SessionKeyStruct storage sessionKey = sessionKeys[_sessionKey]; - // If the signer is a session key that is still valid - if (sessionKey.validUntil == 0) { - return false; - } // Not owner or session key revoked + // If not owner and the session key is revoked, return false + if (sessionKey.validUntil == 0) return false; + // If the signer is a session key that is still valid // Let's first get the selector of the function that the caller is using bytes4 funcSelector = callData[0] | (bytes4(callData[1]) >> 8) | (bytes4(callData[2]) >> 16) | (bytes4(callData[3]) >> 24); if (funcSelector == EXECUTE_SELECTOR) { - if (sessionKey.limit == 0) { - return false; - } // Limit of transactions per sessionKey reached + // Limit of transactions per sessionKey reached + if (sessionKey.limit == 0) return false; + // Deduct one use of the limit for the given session key unchecked { sessionKey.limit = sessionKey.limit - 1; } @@ -145,17 +143,14 @@ abstract contract BaseOpenfortAccount is return false; // All other cases, deny } else if (funcSelector == EXECUTEBATCH_SELECTOR) { (address[] memory toContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); - if (sessionKey.limit < toContracts.length || toContracts.length > 9) { - return false; - } // Limit of transactions per sessionKey reached + // Check if limit of transactions per sessionKey reached + if (sessionKey.limit < toContracts.length || toContracts.length > 9) return false; unchecked { sessionKey.limit = sessionKey.limit - SafeCastUpgradeable.toUint48(toContracts.length); } - // Check if it is a masterSessionKey - if (sessionKey.masterSessionKey) { - return true; - } + // Check if it is a masterSessionKey (no whitelist applies) + if (sessionKey.masterSessionKey) return true; for (uint256 i = 0; i < toContracts.length;) { if (toContracts[i] == address(this)) { From f2e1477e950362dd62dceffd0dec0d0ec3057e2a Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 14 Nov 2023 13:57:02 +0100 Subject: [PATCH 10/83] EIP1271 OF_MSG_TYPEHASH --- contracts/core/BaseOpenfortAccount.sol | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index 1d643c5..fc7cf22 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -39,6 +39,8 @@ abstract contract BaseOpenfortAccount is bytes4 internal constant EXECUTE_SELECTOR = 0xb61d27f6; // bytes4(keccak256("executeBatch(address[],uint256[],bytes[])") bytes4 internal constant EXECUTEBATCH_SELECTOR = 0x47e1da2a; + // keccak256("OpenfortMessage(bytes message)"); + bytes32 private constant OF_MSG_TYPEHASH = 0x0377d315aa54a3da816a164f1ec77274ec694f433648d0b2bed072479f8531fc; /** * Struct like ValidationData (from the EIP-4337) - alpha solution - to keep track of session keys' data @@ -176,19 +178,16 @@ abstract contract BaseOpenfortAccount is */ function isValidSignature(bytes32 _hash, bytes memory _signature) public view override returns (bytes4) { address signer = _hash.recover(_signature); - if (owner() == signer) { - return MAGICVALUE; - } + + if (owner() == signer) return MAGICVALUE; + bytes32 hash = _hash.toEthSignedMessageHash(); signer = hash.recover(_signature); - if (owner() == signer) { - return MAGICVALUE; - } + if (owner() == signer) return MAGICVALUE; + bytes32 digest = _hashTypedDataV4(_hash); signer = digest.recover(_signature); - if (owner() == signer) { - return MAGICVALUE; - } + if (owner() == signer) return MAGICVALUE; SessionKeyStruct storage sessionKey = sessionKeys[signer]; // If the signer is a session key that is still valid From 5b48eace637242b132c943a02a44c42fb5af8a99 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 14 Nov 2023 13:59:11 +0100 Subject: [PATCH 11/83] run fmt --- contracts/core/BaseOpenfortAccount.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index fc7cf22..7ed62d5 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -178,7 +178,7 @@ abstract contract BaseOpenfortAccount is */ function isValidSignature(bytes32 _hash, bytes memory _signature) public view override returns (bytes4) { address signer = _hash.recover(_signature); - + if (owner() == signer) return MAGICVALUE; bytes32 hash = _hash.toEthSignedMessageHash(); From 9f71805fa204f05f5039d934d6e65c3fa96b9508 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 14 Nov 2023 18:45:39 +0100 Subject: [PATCH 12/83] Improving sigs and tests --- contracts/core/BaseOpenfortAccount.sol | 12 +-- .../core/eip6551/EIP6551OpenfortAccount.sol | 92 +++++++++---------- .../core/managed/ManagedOpenfortAccount.sol | 2 +- .../RecoverableOpenfortAccount.sol | 9 +- .../UpgradeableOpenfortAccountTest.t.sol | 74 +++++++++++++++ 5 files changed, 122 insertions(+), 67 deletions(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index 7ed62d5..5e7cbb9 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -174,19 +174,11 @@ abstract contract BaseOpenfortAccount is /* * @notice See EIP-1271 - * Any signature by the owner is valid. Session keys need to sign using EIP712. + * Owner and session keys need to sign using EIP712. */ function isValidSignature(bytes32 _hash, bytes memory _signature) public view override returns (bytes4) { - address signer = _hash.recover(_signature); - - if (owner() == signer) return MAGICVALUE; - - bytes32 hash = _hash.toEthSignedMessageHash(); - signer = hash.recover(_signature); - if (owner() == signer) return MAGICVALUE; - bytes32 digest = _hashTypedDataV4(_hash); - signer = digest.recover(_signature); + address signer = digest.recover(_signature); if (owner() == signer) return MAGICVALUE; SessionKeyStruct storage sessionKey = sessionKeys[signer]; diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index 9dd3082..79176d1 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -8,7 +8,8 @@ import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interface import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; -import "erc6551/src/interfaces/IERC6551Account.sol"; +import {IERC6551Account} from "erc6551/src/interfaces/IERC6551Account.sol"; +import {IERC6551Executable} from "erc6551/src/interfaces/IERC6551Executable.sol"; import {ERC6551AccountLib} from "erc6551/src/lib/ERC6551AccountLib.sol"; import {BaseAccount, UserOperation, IEntryPoint} from "account-abstraction/core/BaseAccount.sol"; import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; @@ -29,6 +30,7 @@ contract EIP6551OpenfortAccount is BaseAccount, Initializable, IERC6551Account, + IERC6551Executable, EIP712Upgradeable, IERC1271Upgradeable, TokenCallbackHandler @@ -39,11 +41,12 @@ contract EIP6551OpenfortAccount is // bytes4(keccak256("isValidSignature(bytes32,bytes)") bytes4 internal constant MAGICVALUE = 0x1626ba7e; + // bytes4(keccak256("execute(address,uint256,bytes,uint8)") + bytes4 internal constant EXECUTE_ERC6551_SELECTOR = 0x51945447; // bytes4(keccak256("execute(address,uint256,bytes)") bytes4 internal constant EXECUTE_SELECTOR = 0xb61d27f6; // bytes4(keccak256("executeBatch(address[],uint256[],bytes[])") bytes4 internal constant EXECUTEBATCH_SELECTOR = 0x47e1da2a; - uint48 internal constant DEFAULT_LIMIT = 100; uint256 public state; @@ -131,27 +134,6 @@ contract EIP6551OpenfortAccount is return IERC721(contractAddress).ownerOf(tokenId); } - /** - * Like execute() from EIP4337. Needed to comply with EIP6551. - */ - function executeCall(address to, uint256 value, bytes calldata data) - external - payable - returns (bytes memory result) - { - _requireFromEntryPointOrOwner(); - // The following code is like _call(), but returning the result value. Consider merging in the future. - bool success; - (success, result) = to.call{value: value}(data); - - if (!success) { - assembly { - revert(add(result, 32), mload(result)) - } - } - ++state; - } - /** * @dev {See IERC6551Account-token} */ @@ -171,19 +153,18 @@ contract EIP6551OpenfortAccount is */ function isValidSessionKey(address _sessionKey, bytes calldata callData) public returns (bool) { SessionKeyStruct storage sessionKey = sessionKeys[_sessionKey]; - // If the signer is a session key that is still valid - if (sessionKey.validUntil == 0) { - return false; - } // Not owner or session key revoked + // If not owner and the session key is revoked, return false + if (sessionKey.validUntil == 0) return false; + // If the signer is a session key that is still valid // Let's first get the selector of the function that the caller is using bytes4 funcSelector = callData[0] | (bytes4(callData[1]) >> 8) | (bytes4(callData[2]) >> 16) | (bytes4(callData[3]) >> 24); - if (funcSelector == EXECUTE_SELECTOR) { - if (sessionKey.limit == 0) { - return false; - } // Limit of transactions per sessionKey reached + if (funcSelector == EXECUTE_SELECTOR || funcSelector == EXECUTE_ERC6551_SELECTOR) { + // Limit of transactions per sessionKey reached + if (sessionKey.limit == 0) return false; + // Deduct one use of the limit for the given session key unchecked { sessionKey.limit = sessionKey.limit - 1; } @@ -208,17 +189,14 @@ contract EIP6551OpenfortAccount is return false; // All other cases, deny } else if (funcSelector == EXECUTEBATCH_SELECTOR) { (address[] memory toContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); - if (sessionKey.limit < toContracts.length || toContracts.length > 9) { - return false; - } // Limit of transactions per sessionKey reached + // Check if limit of transactions per sessionKey reached + if (sessionKey.limit < toContracts.length || toContracts.length > 9) return false; unchecked { sessionKey.limit = sessionKey.limit - SafeCastUpgradeable.toUint48(toContracts.length); } - // Check if it is a masterSessionKey - if (sessionKey.masterSessionKey) { - return true; - } + // Check if it is a masterSessionKey (no whitelist applies) + if (sessionKey.masterSessionKey) return true; for (uint256 i = 0; i < toContracts.length;) { if (toContracts[i] == address(this)) { @@ -244,19 +222,16 @@ contract EIP6551OpenfortAccount is */ function isValidSignature(bytes32 _hash, bytes memory _signature) public view override returns (bytes4) { address signer = _hash.recover(_signature); - if (owner() == signer) { - return MAGICVALUE; - } + + if (owner() == signer) return MAGICVALUE; + bytes32 hash = _hash.toEthSignedMessageHash(); signer = hash.recover(_signature); - if (owner() == signer) { - return MAGICVALUE; - } + if (owner() == signer) return MAGICVALUE; + bytes32 digest = _hashTypedDataV4(_hash); signer = digest.recover(_signature); - if (owner() == signer) { - return MAGICVALUE; - } + if (owner() == signer) return MAGICVALUE; SessionKeyStruct storage sessionKey = sessionKeys[signer]; // If the signer is a session key that is still valid @@ -271,13 +246,32 @@ contract EIP6551OpenfortAccount is } } + /** + * @dev {See IERC6551Executable-execute} + */ + function execute(address _target, uint256 _value, bytes calldata _data, uint8 _operation) + external + payable + override + returns (bytes memory _result) + { + require(_isValidSigner(msg.sender), "Caller is not owner"); + require(_operation == 0, "Only call operations are supported"); + ++state; + bool success; + // solhint-disable-next-line avoid-low-level-calls + (success, _result) = _target.call{value: _value}(_data); + require(success, string(_result)); + return _result; + } + /** * Execute a transaction (called directly from owner, or by entryPoint) */ function execute(address dest, uint256 value, bytes calldata func) external { _requireFromEntryPointOrOwner(); - _call(dest, value, func); ++state; + _call(dest, value, func); } /** @@ -289,11 +283,11 @@ contract EIP6551OpenfortAccount is revert InvalidParameterLength(); } for (uint256 i = 0; i < _target.length;) { + ++state; _call(_target[i], _value[i], _calldata[i]); unchecked { ++i; // gas optimization } - ++state; } } diff --git a/contracts/core/managed/ManagedOpenfortAccount.sol b/contracts/core/managed/ManagedOpenfortAccount.sol index eb29924..63ee5ef 100644 --- a/contracts/core/managed/ManagedOpenfortAccount.sol +++ b/contracts/core/managed/ManagedOpenfortAccount.sol @@ -21,7 +21,7 @@ contract ManagedOpenfortAccount is BaseOpenfortAccount { revert ZeroAddressNotAllowed(); } _transferOwnership(_defaultAdmin); - __EIP712_init("Openfort", "0.4"); + __EIP712_init("Openfort", "0.5"); } /** diff --git a/contracts/core/recoverable/RecoverableOpenfortAccount.sol b/contracts/core/recoverable/RecoverableOpenfortAccount.sol index da2b92d..5a35425 100644 --- a/contracts/core/recoverable/RecoverableOpenfortAccount.sol +++ b/contracts/core/recoverable/RecoverableOpenfortAccount.sol @@ -104,7 +104,7 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradeable { emit EntryPointUpdated(entrypointContract, _entrypoint); _transferOwnership(_defaultAdmin); entrypointContract = _entrypoint; - __EIP712_init("Openfort", "0.4"); + __EIP712_init("Openfort", "0.5"); recoveryPeriod = _recoveryPeriod; lockPeriod = _lockPeriod; @@ -231,6 +231,7 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradeable { * @notice Lets the owner propose a guardian to its Openfort account. * The first guardian is added immediately (see constructor). All following proposals must be confirmed * by calling the confirmGuardianProposal() method. Only the owner can add guardians. + * Guardians must either be an EOA or a contract with an owner() (ERC-173). * @param _guardian The guardian to propose. */ function proposeGuardian(address _guardian) external onlyOwner { @@ -238,12 +239,6 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradeable { if (owner() == _guardian) revert GuardianCannotBeOwner(); if (isGuardian(_guardian)) revert DuplicatedGuardian(); - // Guardians must either be an EOA or a contract with an owner() (ERC-173) - // method that returns an address with a 25000 gas stipend. - // Note that this test is not meant to be strict and can be bypassed by custom malicious contracts. - (bool success,) = _guardian.call{gas: 25000}(abi.encodeWithSignature("owner()")); - require(success, "Must be an EOA or an ownable wallet"); - if ( guardiansConfig.info[_guardian].pending != 0 && block.timestamp < guardiansConfig.info[_guardian].pending + securityWindow diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index e898eb3..28007c0 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -29,6 +29,9 @@ contract UpgradeableOpenfortAccountTest is Test { uint256 private accountAdminPKey; address payable private beneficiary = payable(makeAddr("beneficiary")); + + // bytes4(keccak256("isValidSignature(bytes32,bytes)") + bytes4 internal constant MAGICVALUE = 0x1626ba7e; event AccountCreated(address indexed account, address indexed accountAdmin); @@ -1067,4 +1070,75 @@ contract UpgradeableOpenfortAccountTest is Test { assertEq(address(upgradeableAccount.entryPoint()), newEntryPoint); } + + function testFailIsValidSignature() public { + bytes32 hash = keccak256("Signed by Owner"); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hash); + address signer = ecrecover(hash, v, r, s); + assertEq(accountAdmin, signer); // [PASS] + + bytes memory signature = abi.encodePacked(r, s, v); + signer = ECDSA.recover(hash, signature); + assertEq(accountAdmin, signer); // [PASS] + + bytes4 valid = UpgradeableOpenfortAccount(payable(account)).isValidSignature(hash, signature); + assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! + assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore + } + + function testFailIsValidSignatureMessage() public { + bytes32 hash = keccak256("Signed by Owner"); + bytes32 hashMessage = hash.toEthSignedMessageHash(); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hashMessage); + address signer = ecrecover(hashMessage, v, r, s); + assertEq(accountAdmin, signer); // [PASS] + + bytes memory signature = abi.encodePacked(r, s, v); + signer = ECDSA.recover(hashMessage, signature); + assertEq(accountAdmin, signer); // [PASS] + + bytes4 valid = UpgradeableOpenfortAccount(payable(account)).isValidSignature(hash, signature); + assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! + assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore + } + + + /* + * Auxiliary function to get a valid EIP712 signature using _eip721contract's domains separator, + * a valid hash of the message to sign (_structHash) and a private key (_pk) + */ + function getEIP712SignatureFrom(address _eip721contract, bytes32 _structHash, uint256 _pk) + internal + returns (bytes memory signature721) + { + (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = + IERC5267(_eip721contract).eip712Domain(); + bytes32 _TYPE_HASH = + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + bytes32 domainSeparator = keccak256( + abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) + ); + bytes32 hash712 = domainSeparator.toTypedDataHash(_structHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, hash712); + signature721 = abi.encodePacked(r, s, v); + assertEq(ecrecover(hash712, v, r, s), vm.addr(_pk)); + } + + function testisValidSignatureTyped() public { + bytes32 hash = keccak256("Signed by Owner"); + bytes32 hashMessage = hash.toEthSignedMessageHash(); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hashMessage); + address signer = ecrecover(hashMessage, v, r, s); + assertEq(accountAdmin, signer); // [PASS] + + bytes memory signatures = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + + bytes memory signature = abi.encodePacked(r, s, v); + signer = ECDSA.recover(hashMessage, signature); + assertEq(accountAdmin, signer); // [PASS] + + bytes4 valid = UpgradeableOpenfortAccount(payable(account)).isValidSignature(hash, signature); + assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! + assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore + } } From 8b71e3b20ff2819fd5ab18648d287023543d50e3 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 15 Nov 2023 13:23:42 +0100 Subject: [PATCH 13/83] Refactor for BaseOpenfort and sessionKeys not self --- contracts/core/BaseOpenfortAccount.sol | 38 +- .../core/eip6551/EIP6551OpenfortAccount.sol | 346 +----------------- .../core/managed/ManagedOpenfortAccount.sol | 10 +- .../RecoverableOpenfortAccount.sol | 13 +- .../UpgradeableOpenfortAccount.sol | 11 +- .../mock/MockedV2ManagedOpenfortAccount.sol | 11 +- .../MockedV2UpgradeableOpenfortAccount.sol | 13 +- .../managed/ManagedOpenfortAccountTest.t.sol | 85 +---- .../RecoverableOpenfortAccountTest.t.sol | 88 +---- .../UpgradeableOpenfortAccountTest.t.sol | 52 +-- 10 files changed, 130 insertions(+), 537 deletions(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index 5e7cbb9..8fd6fc4 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -2,7 +2,6 @@ pragma solidity =0.8.19; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; @@ -13,12 +12,11 @@ import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCa import "account-abstraction/core/Helpers.sol" as Helpers; /** - * @title BaseOpenfortAccount (Non-upgradeable) + * @title BaseOpenfortAccount (Non upgradeable by default) * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. * It inherits from: * - BaseAccount to comply with ERC-4337 * - Initializable because accounts are meant to be created using Factories - * - Ownable2StepUpgradeable to have permissions * - EIP712Upgradeable to use typed structured signatures EIP-712 (supporting ERC-5267 too) * - IERC1271Upgradeable for Signature Validation (ERC-1271) * - TokenCallbackHandler to support ERC-777, ERC-721 and ERC-1155 @@ -26,7 +24,6 @@ import "account-abstraction/core/Helpers.sol" as Helpers; abstract contract BaseOpenfortAccount is BaseAccount, Initializable, - Ownable2StepUpgradeable, EIP712Upgradeable, IERC1271Upgradeable, TokenCallbackHandler @@ -39,8 +36,8 @@ abstract contract BaseOpenfortAccount is bytes4 internal constant EXECUTE_SELECTOR = 0xb61d27f6; // bytes4(keccak256("executeBatch(address[],uint256[],bytes[])") bytes4 internal constant EXECUTEBATCH_SELECTOR = 0x47e1da2a; - // keccak256("OpenfortMessage(bytes message)"); - bytes32 private constant OF_MSG_TYPEHASH = 0x0377d315aa54a3da816a164f1ec77274ec694f433648d0b2bed072479f8531fc; + // keccak256("OpenfortMessage(bytes32 hashedMessage)"); + bytes32 private constant OF_MSG_TYPEHASH = 0x57159f03b9efda178eab2037b2ec0b51ce11be0051b8a2a9992c29dc260e4a30; /** * Struct like ValidationData (from the EIP-4337) - alpha solution - to keep track of session keys' data @@ -68,7 +65,7 @@ abstract contract BaseOpenfortAccount is error ZeroAddressNotAllowed(); error NotOwnerOrEntrypoint(); - error NotOwnerOrEntrypointOrSelf(); + error NotOwner(); error InvalidParameterLength(); // solhint-disable-next-line no-empty-blocks @@ -89,14 +86,16 @@ abstract contract BaseOpenfortAccount is } /** - * Require the function call went through EntryPoint, owner or self + * Require the function call went through owner */ - function _requireFromEntryPointOrOwnerorSelf() internal view { - if (msg.sender != address(entryPoint()) && msg.sender != owner() && msg.sender != address(this)) { - revert NotOwnerOrEntrypointOrSelf(); + function _requireFromOwner() internal view { + if (msg.sender != owner()) { + revert NotOwner(); } } + function owner() public view virtual returns (address); + /** * Check current account deposit in the entryPoint */ @@ -177,7 +176,8 @@ abstract contract BaseOpenfortAccount is * Owner and session keys need to sign using EIP712. */ function isValidSignature(bytes32 _hash, bytes memory _signature) public view override returns (bytes4) { - bytes32 digest = _hashTypedDataV4(_hash); + bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, _hash)); + bytes32 digest = _hashTypedDataV4(structHash); address signer = digest.recover(_signature); if (owner() == signer) return MAGICVALUE; @@ -197,7 +197,7 @@ abstract contract BaseOpenfortAccount is /** * Execute a transaction (called directly from owner, or by entryPoint) */ - function execute(address dest, uint256 value, bytes calldata func) external { + function execute(address dest, uint256 value, bytes calldata func) external virtual { _requireFromEntryPointOrOwner(); _call(dest, value, func); } @@ -205,7 +205,10 @@ abstract contract BaseOpenfortAccount is /** * Execute a sequence of transactions. Maximum 9. */ - function executeBatch(address[] calldata _target, uint256[] calldata _value, bytes[] calldata _calldata) external { + function executeBatch(address[] calldata _target, uint256[] calldata _value, bytes[] calldata _calldata) + external + virtual + { _requireFromEntryPointOrOwner(); if (_target.length > 9 || _target.length != _calldata.length || _target.length != _value.length) { revert InvalidParameterLength(); @@ -231,7 +234,8 @@ abstract contract BaseOpenfortAccount is * @param _amount to withdraw * @notice ONLY the owner can call this function (it's not using _requireFromEntryPointOrOwner()) */ - function withdrawDepositTo(address payable _withdrawAddress, uint256 _amount) public onlyOwner { + function withdrawDepositTo(address payable _withdrawAddress, uint256 _amount) external { + _requireFromOwner(); entryPoint().withdrawTo(_withdrawAddress, _amount); } @@ -286,7 +290,7 @@ abstract contract BaseOpenfortAccount is uint48 _limit, address[] calldata _whitelist ) public { - _requireFromEntryPointOrOwnerorSelf(); + _requireFromEntryPointOrOwner(); // Not sure why changing this for a custom error increases gas dramatically require(_whitelist.length < 11, "Whitelist too big"); @@ -317,7 +321,7 @@ abstract contract BaseOpenfortAccount is * @param _key session key to revoke */ function revokeSessionKey(address _key) external { - _requireFromEntryPointOrOwnerorSelf(); + _requireFromEntryPointOrOwner(); if (sessionKeys[_key].validUntil != 0) { sessionKeys[_key].validUntil = 0; sessionKeys[_key].masterSessionKey = false; diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index 79176d1..bb44e24 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -1,17 +1,11 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; -import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; -import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; - import {IERC6551Account} from "erc6551/src/interfaces/IERC6551Account.sol"; import {IERC6551Executable} from "erc6551/src/interfaces/IERC6551Executable.sol"; import {ERC6551AccountLib} from "erc6551/src/lib/ERC6551AccountLib.sol"; -import {BaseAccount, UserOperation, IEntryPoint} from "account-abstraction/core/BaseAccount.sol"; +import {BaseOpenfortAccount, IEntryPoint, SafeCastUpgradeable, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; import "account-abstraction/core/Helpers.sol" as Helpers; @@ -26,62 +20,20 @@ import "account-abstraction/core/Helpers.sol" as Helpers; * - IERC1271Upgradeable for Signature Validation (ERC-1271) * - TokenCallbackHandler to support ERC-777, ERC-721 and ERC-1155 */ -contract EIP6551OpenfortAccount is - BaseAccount, - Initializable, - IERC6551Account, - IERC6551Executable, - EIP712Upgradeable, - IERC1271Upgradeable, - TokenCallbackHandler -{ +contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC6551Executable { using ECDSAUpgradeable for bytes32; address internal entrypointContract; - // bytes4(keccak256("isValidSignature(bytes32,bytes)") - bytes4 internal constant MAGICVALUE = 0x1626ba7e; // bytes4(keccak256("execute(address,uint256,bytes,uint8)") bytes4 internal constant EXECUTE_ERC6551_SELECTOR = 0x51945447; - // bytes4(keccak256("execute(address,uint256,bytes)") - bytes4 internal constant EXECUTE_SELECTOR = 0xb61d27f6; - // bytes4(keccak256("executeBatch(address[],uint256[],bytes[])") - bytes4 internal constant EXECUTEBATCH_SELECTOR = 0x47e1da2a; uint256 public state; - /** - * Struct like ValidationData (from the EIP-4337) - alpha solution - to keep track of session keys' data - * @param validAfter this sessionKey is valid only after this timestamp. - * @param validUntil this sessionKey is valid only after this timestamp. - * @param limit limit of uses remaining - * @param masterSessionKey if set to true, the session key does not have any limitation other than the validity time - * @param whitelising if set to true, the session key has to follow whitelisting rules - * @param whitelist - this session key can only interact with the addresses in the whitelist. - */ - struct SessionKeyStruct { - uint48 validAfter; - uint48 validUntil; - uint48 limit; - bool masterSessionKey; - bool whitelising; - mapping(address => bool) whitelist; - } - - mapping(address => SessionKeyStruct) public sessionKeys; - - event AccountCreated(address indexed creator); - event SessionKeyRegistered(address indexed key); - event SessionKeyRevoked(address indexed key); event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); - error ZeroAddressNotAllowed(); - error NotOwnerOrEntrypoint(); - error NotOwnerOrEntrypointOrSelf(); - error InvalidParameterLength(); - // solhint-disable-next-line no-empty-blocks - receive() external payable virtual {} + receive() external payable override(BaseOpenfortAccount, IERC6551Account) {} constructor() { emit AccountCreated(msg.sender); @@ -101,34 +53,7 @@ contract EIP6551OpenfortAccount is state = 1; } - /** - * Require the function call went through owner - */ - function _requireFromOwner() internal view { - if (msg.sender != owner()) { - revert NotOwnerOrEntrypoint(); - } - } - - /** - * Require the function call went through EntryPoint or owner - */ - function _requireFromEntryPointOrOwner() internal view { - if (msg.sender != address(entryPoint()) && msg.sender != owner()) { - revert NotOwnerOrEntrypoint(); - } - } - - /** - * Require the function call went through EntryPoint, owner or self - */ - function _requireFromEntryPointOrOwnerorSelf() internal view { - if (msg.sender != address(entryPoint()) && msg.sender != owner() && msg.sender != address(this)) { - revert NotOwnerOrEntrypointOrSelf(); - } - } - - function owner() public view virtual returns (address) { + function owner() public view override returns (address) { (uint256 chainId, address contractAddress, uint256 tokenId) = token(); if (chainId != block.chainid) return address(0); return IERC721(contractAddress).ownerOf(tokenId); @@ -141,109 +66,16 @@ contract EIP6551OpenfortAccount is return ERC6551AccountLib.token(); } - /** - * Check current account deposit in the entryPoint - */ - function getDeposit() public view returns (uint256) { - return entryPoint().balanceOf(address(this)); - } - - /* - * @notice Return whether a sessionKey is valid. - */ - function isValidSessionKey(address _sessionKey, bytes calldata callData) public returns (bool) { - SessionKeyStruct storage sessionKey = sessionKeys[_sessionKey]; - // If not owner and the session key is revoked, return false - if (sessionKey.validUntil == 0) return false; - - // If the signer is a session key that is still valid - // Let's first get the selector of the function that the caller is using - bytes4 funcSelector = - callData[0] | (bytes4(callData[1]) >> 8) | (bytes4(callData[2]) >> 16) | (bytes4(callData[3]) >> 24); - - if (funcSelector == EXECUTE_SELECTOR || funcSelector == EXECUTE_ERC6551_SELECTOR) { - // Limit of transactions per sessionKey reached - if (sessionKey.limit == 0) return false; - // Deduct one use of the limit for the given session key - unchecked { - sessionKey.limit = sessionKey.limit - 1; - } - - // Check if it is a masterSessionKey - if (sessionKey.masterSessionKey) { - return true; - } - - // If it is not a masterSessionKey, let's check for whitelisting and reentrancy - address toContract; - (toContract,,) = abi.decode(callData[4:], (address, uint256, bytes)); - if (toContract == address(this)) { - return false; - } // Only masterSessionKey can reenter - - // If there is no whitelist or there is, but the target is whitelisted, return true - if (!sessionKey.whitelising || sessionKey.whitelist[toContract]) { - return true; - } - - return false; // All other cases, deny - } else if (funcSelector == EXECUTEBATCH_SELECTOR) { - (address[] memory toContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); - // Check if limit of transactions per sessionKey reached - if (sessionKey.limit < toContracts.length || toContracts.length > 9) return false; - unchecked { - sessionKey.limit = sessionKey.limit - SafeCastUpgradeable.toUint48(toContracts.length); - } - - // Check if it is a masterSessionKey (no whitelist applies) - if (sessionKey.masterSessionKey) return true; - - for (uint256 i = 0; i < toContracts.length;) { - if (toContracts[i] == address(this)) { - return false; - } // Only masterSessionKey can reenter - if (sessionKey.whitelising && !sessionKey.whitelist[toContracts[i]]) { - return false; - } // One contract's not in the sessionKey's whitelist (if any) - unchecked { - ++i; // gas optimization - } - } - return true; + function isValidSigner(address signer, bytes calldata) external view override returns (bytes4) { + if (_isValidSigner(signer)) { + return IERC6551Account.isValidSigner.selector; } - // If a session key is used for other functions other than execute() or executeBatch(), deny - return false; + return bytes4(0); } - /* - * @notice See EIP-1271 - * Any signature by the owner is valid. Session keys need to sign using EIP712. - */ - function isValidSignature(bytes32 _hash, bytes memory _signature) public view override returns (bytes4) { - address signer = _hash.recover(_signature); - - if (owner() == signer) return MAGICVALUE; - - bytes32 hash = _hash.toEthSignedMessageHash(); - signer = hash.recover(_signature); - if (owner() == signer) return MAGICVALUE; - - bytes32 digest = _hashTypedDataV4(_hash); - signer = digest.recover(_signature); - if (owner() == signer) return MAGICVALUE; - - SessionKeyStruct storage sessionKey = sessionKeys[signer]; - // If the signer is a session key that is still valid - if ( - sessionKey.validUntil == 0 || sessionKey.validAfter > block.timestamp - || sessionKey.validUntil < block.timestamp || sessionKey.limit < 1 - ) { - return 0xffffffff; - } // Not owner or session key revoked - else { - return MAGICVALUE; - } + function _isValidSigner(address signer) internal view returns (bool) { + return signer == owner(); } /** @@ -266,149 +98,16 @@ contract EIP6551OpenfortAccount is } /** - * Execute a transaction (called directly from owner, or by entryPoint) - */ - function execute(address dest, uint256 value, bytes calldata func) external { - _requireFromEntryPointOrOwner(); - ++state; - _call(dest, value, func); - } - - /** - * Execute a sequence of transactions. Maximum 9. + * Update the EntryPoint address */ - function executeBatch(address[] calldata _target, uint256[] calldata _value, bytes[] calldata _calldata) external { - _requireFromEntryPointOrOwner(); - if (_target.length > 9 || _target.length != _calldata.length || _target.length != _value.length) { - revert InvalidParameterLength(); - } - for (uint256 i = 0; i < _target.length;) { - ++state; - _call(_target[i], _value[i], _calldata[i]); - unchecked { - ++i; // gas optimization - } + function updateEntryPoint(address _newEntrypoint) external { + if (_newEntrypoint == address(0)) { + revert ZeroAddressNotAllowed(); } - } - - /** - * Deposit more funds for this account in the entryPoint - */ - function addDeposit() public payable { - entryPoint().depositTo{value: msg.value}(address(this)); - } - - /** - * Withdraw value from the account's deposit - * @param withdrawAddress target to send to - * @param amount to withdraw - * @notice ONLY the owner can call this function (it's not using _requireFromEntryPointOrOwner()) - */ - function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public { _requireFromOwner(); ++state; - entryPoint().withdrawTo(withdrawAddress, amount); - } - - /** - * @dev Call a target contract and reverts if it fails. - */ - function _call(address _target, uint256 value, bytes calldata _calldata) internal { - (bool success, bytes memory result) = _target.call{value: value}(_calldata); - if (!success) { - assembly { - revert(add(result, 32), mload(result)) - } - } - ++state; - } - - /** - * @inheritdoc BaseAccount - */ - function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash) - internal - override - returns (uint256 validationData) - { - bytes32 hash = userOpHash.toEthSignedMessageHash(); - address signer = hash.recover(userOp.signature); - - // If the userOp was signed by the owner, allow straightaway - if (owner() == signer) { - return 0; - } - - // Check if the session key is valid according to the data in the userOp - if (isValidSessionKey(signer, userOp.callData)) { - return Helpers._packValidationData(false, sessionKeys[signer].validUntil, sessionKeys[signer].validAfter); - } - - return SIG_VALIDATION_FAILED; - } - - function isValidSigner(address signer, bytes calldata) external view virtual returns (bytes4) { - if (_isValidSigner(signer)) return IERC6551Account.isValidSigner.selector; - return bytes4(0); - } - - function _isValidSigner(address signer) internal view virtual returns (bool) { - return signer == owner(); - } - - /** - * Register a session key to the account - * @param _key session key to register - * @param _validAfter - this session key is valid only after this timestamp. - * @param _validUntil - this session key is valid only up to this timestamp. - * @param _limit - limit of uses remaining. - * @param _whitelist - this session key can only interact with the addresses in the _whitelist. - */ - function registerSessionKey( - address _key, - uint48 _validAfter, - uint48 _validUntil, - uint48 _limit, - address[] calldata _whitelist - ) public { - _requireFromEntryPointOrOwnerorSelf(); - - // Not sure why changing this for a custom error increases gas dramatically - require(_whitelist.length < 11, "Whitelist too big"); - uint256 i = 0; - for (i; i < _whitelist.length;) { - sessionKeys[_key].whitelist[_whitelist[i]] = true; - unchecked { - ++i; // gas optimization - } - } - if (i != 0) sessionKeys[_key].whitelising = true; - else if (_limit == 2 ** 48 - 1) sessionKeys[_key].masterSessionKey = true; - - sessionKeys[_key].validAfter = _validAfter; - sessionKeys[_key].validUntil = _validUntil; - sessionKeys[_key].limit = _limit; - sessionKeys[_key].masterSessionKey = false; - ++state; - emit SessionKeyRegistered(_key); - } - - /** - * Revoke a session key from the account - * @param _key session key to revoke - */ - function revokeSessionKey(address _key) external { - _requireFromEntryPointOrOwnerorSelf(); - ++state; - if (sessionKeys[_key].validUntil != 0) { - sessionKeys[_key].validUntil = 0; - sessionKeys[_key].masterSessionKey = false; - emit SessionKeyRevoked(_key); - } - } - - function version() external pure virtual returns (uint256) { - return 1; + emit EntryPointUpdated(entrypointContract, _newEntrypoint); + entrypointContract = _newEntrypoint; } /** @@ -417,17 +116,4 @@ contract EIP6551OpenfortAccount is function entryPoint() public view override returns (IEntryPoint) { return IEntryPoint(entrypointContract); } - - /** - * Update the EntryPoint address - */ - function updateEntryPoint(address _newEntrypoint) external { - if (_newEntrypoint == address(0)) { - revert ZeroAddressNotAllowed(); - } - _requireFromOwner(); - ++state; - emit EntryPointUpdated(entrypointContract, _newEntrypoint); - entrypointContract = _newEntrypoint; - } } diff --git a/contracts/core/managed/ManagedOpenfortAccount.sol b/contracts/core/managed/ManagedOpenfortAccount.sol index 63ee5ef..4aa0f01 100644 --- a/contracts/core/managed/ManagedOpenfortAccount.sol +++ b/contracts/core/managed/ManagedOpenfortAccount.sol @@ -2,6 +2,10 @@ pragma solidity =0.8.19; // Base account contract to inherit from and EntryPoint interface +import { + Ownable2StepUpgradeable, + OwnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {BaseOpenfortAccount, IEntryPoint} from "../BaseOpenfortAccount.sol"; /** @@ -10,7 +14,7 @@ import {BaseOpenfortAccount, IEntryPoint} from "../BaseOpenfortAccount.sol"; * It inherits from: * - BaseOpenfortAccount */ -contract ManagedOpenfortAccount is BaseOpenfortAccount { +contract ManagedOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable { address private constant ENTRYPOINTCONTRACT = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; /* @@ -24,6 +28,10 @@ contract ManagedOpenfortAccount is BaseOpenfortAccount { __EIP712_init("Openfort", "0.5"); } + function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { + return OwnableUpgradeable.owner(); + } + /** * Return the current EntryPoint */ diff --git a/contracts/core/recoverable/RecoverableOpenfortAccount.sol b/contracts/core/recoverable/RecoverableOpenfortAccount.sol index 5a35425..33ae706 100644 --- a/contracts/core/recoverable/RecoverableOpenfortAccount.sol +++ b/contracts/core/recoverable/RecoverableOpenfortAccount.sol @@ -2,10 +2,15 @@ pragma solidity =0.8.19; // Base account contract to inherit from and EntryPoint interface -import {BaseOpenfortAccount, IEntryPoint, SafeCastUpgradeable, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; +import { + Ownable2StepUpgradeable, + OwnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {BaseOpenfortAccount, IEntryPoint, SafeCastUpgradeable, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; + /** * @title RecoverableOpenfortAccount * @notice Openfort account with session keys, guardians and pausability following the ERC-4337 standard. @@ -13,7 +18,7 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; * - BaseOpenfortAccount * - UUPSUpgradeable */ -contract RecoverableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradeable { +contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable, UUPSUpgradeable { using ECDSAUpgradeable for bytes32; address internal entrypointContract; @@ -127,6 +132,10 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradeable { return IEntryPoint(entrypointContract); } + function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { + return OwnableUpgradeable.owner(); + } + /** * Update the EntryPoint address */ diff --git a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol index 5539661..b6ad9d9 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol @@ -3,6 +3,10 @@ pragma solidity =0.8.19; // Base account contract to inherit from and EntryPoint interface import {BaseOpenfortAccount, IEntryPoint} from "../BaseOpenfortAccount.sol"; +import { + Ownable2StepUpgradeable, + OwnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; /** @@ -10,9 +14,10 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. * It inherits from: * - BaseOpenfortAccount + * - Ownable2StepUpgradeable * - UUPSUpgradeable */ -contract UpgradeableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradeable { +contract UpgradeableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable, UUPSUpgradeable { address internal entrypointContract; event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); @@ -39,6 +44,10 @@ contract UpgradeableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradeable { return IEntryPoint(entrypointContract); } + function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { + return OwnableUpgradeable.owner(); + } + /** * Update the EntryPoint address */ diff --git a/contracts/mock/MockedV2ManagedOpenfortAccount.sol b/contracts/mock/MockedV2ManagedOpenfortAccount.sol index 6e6689a..d6e5786 100644 --- a/contracts/mock/MockedV2ManagedOpenfortAccount.sol +++ b/contracts/mock/MockedV2ManagedOpenfortAccount.sol @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; +import { + Ownable2StepUpgradeable, + OwnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; + // Base account contract to inherit from import {BaseOpenfortAccount, IEntryPoint} from "../core/BaseOpenfortAccount.sol"; @@ -10,7 +15,7 @@ import {BaseOpenfortAccount, IEntryPoint} from "../core/BaseOpenfortAccount.sol" * It inherits from: * - BaseOpenfortAccount */ -contract MockedV2ManagedOpenfortAccount is BaseOpenfortAccount { +contract MockedV2ManagedOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable { address private constant ENTRYPOINTCONTRACT = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; /* @@ -27,6 +32,10 @@ contract MockedV2ManagedOpenfortAccount is BaseOpenfortAccount { return 2; } + function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { + return OwnableUpgradeable.owner(); + } + /** * Return the current EntryPoint */ diff --git a/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol b/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol index d32ba21..3f57d8f 100644 --- a/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol +++ b/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol @@ -1,9 +1,14 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; +import { + Ownable2StepUpgradeable, + OwnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + // Base account contract to inherit from import {BaseOpenfortAccount, IEntryPoint} from "../core/BaseOpenfortAccount.sol"; -import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; /** * @title MockedV2UpgradeableOpenfortAccount @@ -12,7 +17,7 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U * - BaseOpenfortAccount * - UUPSUpgradeable */ -contract MockedV2UpgradeableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradeable { +contract MockedV2UpgradeableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable, UUPSUpgradeable { address internal entrypointContract; /* * @notice Initialize the smart contract wallet. @@ -32,6 +37,10 @@ contract MockedV2UpgradeableOpenfortAccount is BaseOpenfortAccount, UUPSUpgradea return 2; } + function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { + return OwnableUpgradeable.owner(); + } + /** * Return the current EntryPoint */ diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index f6ea748..68e55ce 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -349,12 +349,10 @@ contract ManagedOpenfortAccountTest is Test { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - UserOperation[] memory userOp = _setupUserOpExecute( + UserOperation[] memory userOp = _setupUserOp( account, accountAdminPKey, bytes(""), - account, - 0, abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", sessionKey, @@ -389,8 +387,9 @@ contract ManagedOpenfortAccountTest is Test { /* * Register a master sessionKey via userOp calling the execute() function * using the EntryPoint (userOp). Then use that sessionKey to register a second one + * Should not be allowed: session keys cannot register new session keys! */ - function testRegisterSessionKeyViaEntrypoint2ndKey() public { + function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); @@ -399,12 +398,10 @@ contract ManagedOpenfortAccountTest is Test { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - UserOperation[] memory userOp = _setupUserOpExecute( + UserOperation[] memory userOp = _setupUserOp( account, accountAdminPKey, bytes(""), - account, - 0, abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", sessionKey, @@ -439,18 +436,16 @@ contract ManagedOpenfortAccountTest is Test { uint256 sessionKeyPrivKeyAttack; (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); - userOp = _setupUserOpExecute( + userOp = _setupUserOp( account, sessionKeyPrivKey, bytes(""), - account, - 0, abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", sessionKeyAttack, 0, 2 ** 48 - 1, - 100, + 2 ** 48 - 1, emptyWhitelist ) ); @@ -461,73 +456,6 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.handleOps(userOp, beneficiary); } - /* - * Register a limited sessionKey via userOp calling the execute() function - * using the EntryPoint (userOp). Then use that sessionKey to register a second one - */ - function testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - account, - 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1, 10) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - - userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - - // Verify that the registered key is not a MasterKey nor has whitelisting - bool isMasterKey; - bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = ManagedOpenfortAccount(payable(account)).sessionKeys(sessionKey); - assert(!isMasterKey); - assert(!isWhitelisted); - - address sessionKeyAttack; - uint256 sessionKeyPrivKeyAttack; - (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); - - userOp = _setupUserOpExecute( - account, - sessionKeyPrivKey, - bytes(""), - account, - 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKeyAttack, 0, 2 ** 48 - 1) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - } - /* * Should fail, try to use a sessionKey that is expired. */ @@ -935,6 +863,7 @@ contract ManagedOpenfortAccountTest is Test { uint256 accountAdmin2PKey; (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); + assertEq(ManagedOpenfortAccount(payable(account)).owner(), accountAdmin); vm.expectRevert("Ownable: caller is not the owner"); ManagedOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); diff --git a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol index 76d637e..06a1aca 100644 --- a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol +++ b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol @@ -63,6 +63,8 @@ contract RecoverableOpenfortAccountTest is Test { // keccak256("Recover(address recoveryAddress,uint64 executeAfter,uint32 guardiansRequired)"); bytes32 RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; + bytes32 private constant _TYPE_HASH = + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); /* * Auxiliary function to generate a userOP @@ -423,12 +425,10 @@ contract RecoverableOpenfortAccountTest is Test { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - UserOperation[] memory userOp = _setupUserOpExecute( + UserOperation[] memory userOp = _setupUserOp( account, accountAdminPKey, bytes(""), - account, - 0, abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", sessionKey, @@ -463,8 +463,9 @@ contract RecoverableOpenfortAccountTest is Test { /* * Register a master sessionKey via userOp calling the execute() function * using the EntryPoint (userOp). Then use that sessionKey to register a second one + * Should not be allowed: session keys cannot register new session keys! */ - function testRegisterSessionKeyViaEntrypoint2ndKey() public { + function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); @@ -473,12 +474,10 @@ contract RecoverableOpenfortAccountTest is Test { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - UserOperation[] memory userOp = _setupUserOpExecute( + UserOperation[] memory userOp = _setupUserOp( account, accountAdminPKey, bytes(""), - account, - 0, abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", sessionKey, @@ -513,18 +512,16 @@ contract RecoverableOpenfortAccountTest is Test { uint256 sessionKeyPrivKeyAttack; (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); - userOp = _setupUserOpExecute( + userOp = _setupUserOp( account, sessionKeyPrivKey, bytes(""), - account, - 0, abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", sessionKeyAttack, 0, 2 ** 48 - 1, - 100, + 2 ** 48 - 1, emptyWhitelist ) ); @@ -535,73 +532,6 @@ contract RecoverableOpenfortAccountTest is Test { entryPoint.handleOps(userOp, beneficiary); } - /* - * Register a limited sessionKey via userOp calling the execute() function - * using the EntryPoint (userOp). Then use that sessionKey to register a second one - */ - function testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - account, - 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1, 10) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - - userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - - // Verify that the registered key is not a MasterKey nor has whitelisting - bool isMasterKey; - bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = RecoverableOpenfortAccount(payable(account)).sessionKeys(sessionKey); - assert(!isMasterKey); - assert(!isWhitelisted); - - address sessionKeyAttack; - uint256 sessionKeyPrivKeyAttack; - (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); - - userOp = _setupUserOpExecute( - account, - sessionKeyPrivKey, - bytes(""), - account, - 0, - abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKeyAttack, 0, 2 ** 48 - 1) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - } - /* * Should fail, try to use a sessionKey that is expired. */ @@ -2059,8 +1989,6 @@ contract RecoverableOpenfortAccountTest is Test { { (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = IERC5267(_eip721contract).eip712Domain(); - bytes32 _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); bytes32 domainSeparator = keccak256( abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) ); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 28007c0..e6a06a5 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -3,6 +3,7 @@ pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {TestToken} from "account-abstraction/test/TestToken.sol"; @@ -29,9 +30,13 @@ contract UpgradeableOpenfortAccountTest is Test { uint256 private accountAdminPKey; address payable private beneficiary = payable(makeAddr("beneficiary")); - + // bytes4(keccak256("isValidSignature(bytes32,bytes)") bytes4 internal constant MAGICVALUE = 0x1626ba7e; + // keccak256("OpenfortMessage(bytes32 hashedMessage)"); + bytes32 private constant OF_MSG_TYPEHASH = 0x57159f03b9efda178eab2037b2ec0b51ce11be0051b8a2a9992c29dc260e4a30; + bytes32 private constant _TYPE_HASH = + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); event AccountCreated(address indexed account, address indexed accountAdmin); @@ -351,12 +356,10 @@ contract UpgradeableOpenfortAccountTest is Test { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - UserOperation[] memory userOp = _setupUserOpExecute( + UserOperation[] memory userOp = _setupUserOp( account, accountAdminPKey, bytes(""), - account, - 0, abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", sessionKey, @@ -391,8 +394,9 @@ contract UpgradeableOpenfortAccountTest is Test { /* * Register a master sessionKey via userOp calling the execute() function * using the EntryPoint (userOp). Then use that sessionKey to register a second one + * Should not be allowed: session keys cannot register new session keys! */ - function testRegisterSessionKeyViaEntrypoint2ndKey() public { + function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { // Verify that the counter is stil set to 0 assertEq(testCounter.counters(account), 0); @@ -401,12 +405,10 @@ contract UpgradeableOpenfortAccountTest is Test { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - UserOperation[] memory userOp = _setupUserOpExecute( + UserOperation[] memory userOp = _setupUserOp( account, accountAdminPKey, bytes(""), - account, - 0, abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", sessionKey, @@ -441,18 +443,16 @@ contract UpgradeableOpenfortAccountTest is Test { uint256 sessionKeyPrivKeyAttack; (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); - userOp = _setupUserOpExecute( + userOp = _setupUserOp( account, sessionKeyPrivKey, bytes(""), - account, - 0, abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", sessionKeyAttack, 0, 2 ** 48 - 1, - 100, + 2 ** 48 - 1, emptyWhitelist ) ); @@ -1102,7 +1102,6 @@ contract UpgradeableOpenfortAccountTest is Test { assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore } - /* * Auxiliary function to get a valid EIP712 signature using _eip721contract's domains separator, * a valid hash of the message to sign (_structHash) and a private key (_pk) @@ -1113,8 +1112,6 @@ contract UpgradeableOpenfortAccountTest is Test { { (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = IERC5267(_eip721contract).eip712Domain(); - bytes32 _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); bytes32 domainSeparator = keccak256( abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) ); @@ -1125,20 +1122,25 @@ contract UpgradeableOpenfortAccountTest is Test { } function testisValidSignatureTyped() public { - bytes32 hash = keccak256("Signed by Owner"); - bytes32 hashMessage = hash.toEthSignedMessageHash(); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hashMessage); - address signer = ecrecover(hashMessage, v, r, s); - assertEq(accountAdmin, signer); // [PASS] + string memory messageToSign = "Signed by Owner"; + bytes32 hash = keccak256(abi.encodePacked(messageToSign)); - bytes memory signatures = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, hash)); + + (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = + IERC5267(account).eip712Domain(); + + bytes32 domainSeparator = keccak256( + abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) + ); + + bytes memory signature = getEIP712SignatureFrom(account, structHash, accountAdminPKey); + bytes32 hash712 = domainSeparator.toTypedDataHash(structHash); + address signer = hash712.recover(signature); - bytes memory signature = abi.encodePacked(r, s, v); - signer = ECDSA.recover(hashMessage, signature); assertEq(accountAdmin, signer); // [PASS] bytes4 valid = UpgradeableOpenfortAccount(payable(account)).isValidSignature(hash, signature); - assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! - assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore + assertEq(valid, MAGICVALUE); // SHOULD PASS } } From be88daa7efb976836100209a2f28459e776d07d3 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 15 Nov 2023 14:04:53 +0100 Subject: [PATCH 14/83] More adapted refactors --- contracts/core/BaseOpenfortAccount.sol | 6 ++-- .../core/eip6551/EIP6551OpenfortAccount.sol | 34 +++++++++++++------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index 8fd6fc4..dd6b58c 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -197,7 +197,7 @@ abstract contract BaseOpenfortAccount is /** * Execute a transaction (called directly from owner, or by entryPoint) */ - function execute(address dest, uint256 value, bytes calldata func) external virtual { + function execute(address dest, uint256 value, bytes calldata func) public virtual { _requireFromEntryPointOrOwner(); _call(dest, value, func); } @@ -206,7 +206,7 @@ abstract contract BaseOpenfortAccount is * Execute a sequence of transactions. Maximum 9. */ function executeBatch(address[] calldata _target, uint256[] calldata _value, bytes[] calldata _calldata) - external + public virtual { _requireFromEntryPointOrOwner(); @@ -222,7 +222,7 @@ abstract contract BaseOpenfortAccount is } /** - * Deposit more funds for this account in the EntryPoint + * Deposit funds for this account in the EntryPoint */ function addDeposit() public payable { entryPoint().depositTo{value: msg.value}(address(this)); diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index bb44e24..cab97f5 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -5,7 +5,7 @@ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC6551Account} from "erc6551/src/interfaces/IERC6551Account.sol"; import {IERC6551Executable} from "erc6551/src/interfaces/IERC6551Executable.sol"; import {ERC6551AccountLib} from "erc6551/src/lib/ERC6551AccountLib.sol"; -import {BaseOpenfortAccount, IEntryPoint, SafeCastUpgradeable, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; +import {BaseOpenfortAccount, IEntryPoint, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; import "account-abstraction/core/Helpers.sol" as Helpers; @@ -13,12 +13,9 @@ import "account-abstraction/core/Helpers.sol" as Helpers; * @title EIP6551OpenfortAccount (Non-upgradeable) * @notice Smart contract wallet with session keys following the ERC-4337 and EIP-6551 standards. * It inherits from: - * - BaseAccount to comply with ERC-4337 - * - Initializable because accounts are meant to be created using Factories + * - BaseOpenfortAccount to comply with ERC-4337 * - IERC6551Account to have permissions using ERC-721 tokens - * - EIP712Upgradeable to use typed structured signatures EIP-712 (supporting ERC-5267 too) - * - IERC1271Upgradeable for Signature Validation (ERC-1271) - * - TokenCallbackHandler to support ERC-777, ERC-721 and ERC-1155 + * - IERC6551Executable */ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC6551Executable { using ECDSAUpgradeable for bytes32; @@ -67,10 +64,7 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 } function isValidSigner(address signer, bytes calldata) external view override returns (bytes4) { - if (_isValidSigner(signer)) { - return IERC6551Account.isValidSigner.selector; - } - + if (_isValidSigner(signer)) return IERC6551Account.isValidSigner.selector; return bytes4(0); } @@ -87,7 +81,6 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 override returns (bytes memory _result) { - require(_isValidSigner(msg.sender), "Caller is not owner"); require(_operation == 0, "Only call operations are supported"); ++state; bool success; @@ -97,6 +90,25 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 return _result; } + /** + * Execute a transaction (called directly from owner, or by entryPoint) + */ + function execute(address dest, uint256 value, bytes calldata func) public override { + ++state; + super.execute(dest, value, func); + } + + /** + * Execute a sequence of transactions. Maximum 9. + */ + function executeBatch(address[] calldata _target, uint256[] calldata _value, bytes[] calldata _calldata) + public + override + { + state += _target.length; + super.executeBatch(_target, _value, _calldata); + } + /** * Update the EntryPoint address */ From a6997d2948564b248ee6e2621542dbec6cb3aee5 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 15 Nov 2023 14:13:00 +0100 Subject: [PATCH 15/83] Invalidate sessionKeys when owner changed --- contracts/core/BaseOpenfortAccount.sol | 12 ++++++++++-- .../core/managed/ManagedOpenfortAccountTest.t.sol | 4 ++-- .../recoverable/RecoverableOpenfortAccountTest.t.sol | 2 +- .../upgradeable/UpgradeableOpenfortAccountTest.t.sol | 4 ++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index dd6b58c..58da9ba 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -55,6 +55,7 @@ abstract contract BaseOpenfortAccount is bool masterSessionKey; bool whitelising; mapping(address => bool) whitelist; + address registrarAddress; } mapping(address => SessionKeyStruct) public sessionKeys; @@ -111,6 +112,9 @@ abstract contract BaseOpenfortAccount is // If not owner and the session key is revoked, return false if (sessionKey.validUntil == 0) return false; + // If the sessionKey was not registered by the owner, return false + if (sessionKey.registrarAddress != owner()) return false; + // If the signer is a session key that is still valid // Let's first get the selector of the function that the caller is using bytes4 funcSelector = @@ -189,7 +193,9 @@ abstract contract BaseOpenfortAccount is ) { return 0xffffffff; } // Not owner or session key revoked - else { + else if (sessionKey.registrarAddress != owner()) { + return 0xffffffff; + } else { return MAGICVALUE; } } @@ -292,7 +298,6 @@ abstract contract BaseOpenfortAccount is ) public { _requireFromEntryPointOrOwner(); - // Not sure why changing this for a custom error increases gas dramatically require(_whitelist.length < 11, "Whitelist too big"); uint256 i = 0; for (i; i < _whitelist.length;) { @@ -302,6 +307,7 @@ abstract contract BaseOpenfortAccount is } } if (i > 0) { + // If there was some whitelisting, it is not a masterSessionKey sessionKeys[_key].whitelising = true; sessionKeys[_key].masterSessionKey = false; } else { @@ -312,6 +318,7 @@ abstract contract BaseOpenfortAccount is sessionKeys[_key].validAfter = _validAfter; sessionKeys[_key].validUntil = _validUntil; sessionKeys[_key].limit = _limit; + sessionKeys[_key].registrarAddress = owner(); emit SessionKeyRegistered(_key); } @@ -325,6 +332,7 @@ abstract contract BaseOpenfortAccount is if (sessionKeys[_key].validUntil != 0) { sessionKeys[_key].validUntil = 0; sessionKeys[_key].masterSessionKey = false; + sessionKeys[_key].registrarAddress = address(0); emit SessionKeyRevoked(_key); } } diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 68e55ce..bca7926 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -700,7 +700,7 @@ contract ManagedOpenfortAccountTest is Test { // Verify that the registered key is not a MasterKey but has whitelisting bool isMasterKey; bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = ManagedOpenfortAccount(payable(account)).sessionKeys(sessionKey); + (,,, isMasterKey, isWhitelisted,) = ManagedOpenfortAccount(payable(account)).sessionKeys(sessionKey); assert(!isMasterKey); assert(isWhitelisted); @@ -746,7 +746,7 @@ contract ManagedOpenfortAccountTest is Test { // Verify that the registered key is not a MasterKey but has whitelisting bool isMasterKey; bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = ManagedOpenfortAccount(payable(account)).sessionKeys(sessionKey); + (,,, isMasterKey, isWhitelisted,) = ManagedOpenfortAccount(payable(account)).sessionKeys(sessionKey); assert(!isMasterKey); assert(isWhitelisted); diff --git a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol index 06a1aca..3c3a9d3 100644 --- a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol +++ b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol @@ -776,7 +776,7 @@ contract RecoverableOpenfortAccountTest is Test { // Verify that the registered key is not a MasterKey but has whitelisting bool isMasterKey; bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = RecoverableOpenfortAccount(payable(account)).sessionKeys(sessionKey); + (,,, isMasterKey, isWhitelisted,) = RecoverableOpenfortAccount(payable(account)).sessionKeys(sessionKey); assert(!isMasterKey); assert(isWhitelisted); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index e6a06a5..e2f556f 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -507,7 +507,7 @@ contract UpgradeableOpenfortAccountTest is Test { // Verify that the registered key is not a MasterKey nor has whitelisting bool isMasterKey; bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = UpgradeableOpenfortAccount(payable(account)).sessionKeys(sessionKey); + (,,, isMasterKey, isWhitelisted,) = UpgradeableOpenfortAccount(payable(account)).sessionKeys(sessionKey); assert(!isMasterKey); assert(!isWhitelisted); @@ -774,7 +774,7 @@ contract UpgradeableOpenfortAccountTest is Test { // Verify that the registered key is not a MasterKey but has whitelisting bool isMasterKey; bool isWhitelisted; - (,,, isMasterKey, isWhitelisted) = UpgradeableOpenfortAccount(payable(account)).sessionKeys(sessionKey); + (,,, isMasterKey, isWhitelisted,) = UpgradeableOpenfortAccount(payable(account)).sessionKeys(sessionKey); assert(!isMasterKey); assert(isWhitelisted); From 17e8adc188507b8f72c4134ec5f4d16660bc8356 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 15 Nov 2023 17:54:23 +0100 Subject: [PATCH 16/83] Improving imports and linting --- .solhint.json | 22 +++++++++++++------ .vscode/settings.json | 4 +++- contracts/core/BaseOpenfortAccount.sol | 13 +++++------ .../core/eip6551/EIP6551OpenfortAccount.sol | 22 ++++++++++++++++--- contracts/mock/Rewards.sol | 7 ++---- contracts/mock/SimpleNFT.sol | 3 --- contracts/mock/USDC.sol | 7 ++---- contracts/mock/VipNFT.sol | 5 +---- foundry.toml | 9 ++++++++ package.json | 2 +- .../RecoverableOpenfortAccountTest.t.sol | 2 +- .../UpgradeableOpenfortAccountTest.t.sol | 4 ++-- yarn.lock | 17 +++++++++----- 13 files changed, 72 insertions(+), 45 deletions(-) diff --git a/.solhint.json b/.solhint.json index 9e1936c..8cb915f 100644 --- a/.solhint.json +++ b/.solhint.json @@ -1,9 +1,17 @@ { - "extends": "solhint:recommended", - "rules": { - "compiler-version": ["error",">=0.7.5"], - "func-visibility": ["off",{"ignoreConstructors":true}], - "mark-callable-contracts": ["off"] - } + "extends": "solhint:recommended", + "rules": { + "avoid-low-level-calls": "off", + "code-complexity": ["error", 9], + "compiler-version": ["error", ">=0.8.19"], + "contract-name-camelcase": "off", + "const-name-snakecase": "off", + "func-name-mixedcase": "off", + "func-visibility": ["error", { "ignoreConstructors": true }], + "max-line-length": ["error", 123], + "named-parameters-mapping": "warn", + "no-empty-blocks": "off", + "not-rely-on-time": "off", + "var-name-mixedcase": "off" } - \ No newline at end of file +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b5666c3..85fb756 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,5 @@ { - "solidity.compileUsingRemoteVersion": "v0.8.19+commit.7dd6d404" + "solidity.compileUsingRemoteVersion": "v0.8.19+commit.7dd6d404", + "solidity.defaultCompiler": "remote", + "solidity.compileUsingLocalVersion": "/Users/eloimanuel/Projects/Openfort/openfort-contracts/soljson-v0.8.19+commit.7dd6d404.js" } \ No newline at end of file diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index 58da9ba..d05de21 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -6,10 +6,9 @@ import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/crypto import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; - -import {BaseAccount, UserOperation, IEntryPoint} from "account-abstraction/core/BaseAccount.sol"; +import {BaseAccount, UserOperation, IEntryPoint, UserOperationLib} from "account-abstraction/core/BaseAccount.sol"; import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; -import "account-abstraction/core/Helpers.sol" as Helpers; +import {_packValidationData} from "account-abstraction/core/Helpers.sol"; /** * @title BaseOpenfortAccount (Non upgradeable by default) @@ -69,7 +68,6 @@ abstract contract BaseOpenfortAccount is error NotOwner(); error InvalidParameterLength(); - // solhint-disable-next-line no-empty-blocks receive() external payable virtual {} constructor() { @@ -265,17 +263,16 @@ abstract contract BaseOpenfortAccount is override returns (uint256 validationData) { + // require(entryPoint().getUserOpHash(userOp) == userOpHash, "MEEECK!"); bytes32 hash = userOpHash.toEthSignedMessageHash(); address signer = hash.recover(userOp.signature); // If the userOp was signed by the owner, allow straightaway - if (owner() == signer) { - return 0; - } + if (owner() == signer) return 0; // Check if the session key is valid according to the data in the userOp if (isValidSessionKey(signer, userOp.callData)) { - return Helpers._packValidationData(false, sessionKeys[signer].validUntil, sessionKeys[signer].validAfter); + return _packValidationData(false, sessionKeys[signer].validUntil, sessionKeys[signer].validAfter); } return SIG_VALIDATION_FAILED; diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index cab97f5..e216443 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -2,12 +2,15 @@ pragma solidity =0.8.19; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC6551Account} from "erc6551/src/interfaces/IERC6551Account.sol"; import {IERC6551Executable} from "erc6551/src/interfaces/IERC6551Executable.sol"; import {ERC6551AccountLib} from "erc6551/src/lib/ERC6551AccountLib.sol"; -import {BaseOpenfortAccount, IEntryPoint, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; -import "account-abstraction/core/Helpers.sol" as Helpers; + +import {BaseOpenfortAccount, IEntryPoint, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; /** * @title EIP6551OpenfortAccount (Non-upgradeable) @@ -29,7 +32,6 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); - // solhint-disable-next-line no-empty-blocks receive() external payable override(BaseOpenfortAccount, IERC6551Account) {} constructor() { @@ -50,6 +52,9 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 state = 1; } + /* + * Returns the address of the owner + */ function owner() public view override returns (address) { (uint256 chainId, address contractAddress, uint256 tokenId) = token(); if (chainId != block.chainid) return address(0); @@ -63,6 +68,9 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 return ERC6551AccountLib.token(); } + /** + * @dev {See IERC6551Account-isValidSigner} + */ function isValidSigner(address signer, bytes calldata) external view override returns (bytes4) { if (_isValidSigner(signer)) return IERC6551Account.isValidSigner.selector; return bytes4(0); @@ -128,4 +136,12 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 function entryPoint() public view override returns (IEntryPoint) { return IEntryPoint(entrypointContract); } + + function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { + return ( + interfaceId == type(IERC6551Account).interfaceId || interfaceId == type(IERC6551Executable).interfaceId + || interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC721Receiver).interfaceId + || interfaceId == type(IERC165).interfaceId + ); + } } diff --git a/contracts/mock/Rewards.sol b/contracts/mock/Rewards.sol index d014a1b..ea515c6 100644 --- a/contracts/mock/Rewards.sol +++ b/contracts/mock/Rewards.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract Rewards is ERC20 { - constructor() - // solhint-disable-next-line no-empty-blocks - ERC20("GEMS", "GEMS") - {} + constructor() ERC20("GEMS", "GEMS") {} function claim(uint256 amount) external { _mint(msg.sender, amount); diff --git a/contracts/mock/SimpleNFT.sol b/contracts/mock/SimpleNFT.sol index bcca742..5f08f7c 100644 --- a/contracts/mock/SimpleNFT.sol +++ b/contracts/mock/SimpleNFT.sol @@ -1,9 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity =0.8.19; -/* solhint-disable reason-string */ -/* solhint-disable no-empty-blocks */ - import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; // A simple ERC721 contract diff --git a/contracts/mock/USDC.sol b/contracts/mock/USDC.sol index 878e7a0..c523585 100644 --- a/contracts/mock/USDC.sol +++ b/contracts/mock/USDC.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract USDC is ERC20 { - constructor() - // solhint-disable-next-line no-empty-blocks - ERC20("USDC", "USDC") - {} + constructor() ERC20("USDC", "USDC") {} function mint(address sender, uint256 amount) external { _mint(sender, amount); diff --git a/contracts/mock/VipNFT.sol b/contracts/mock/VipNFT.sol index 503b92e..679eb4b 100644 --- a/contracts/mock/VipNFT.sol +++ b/contracts/mock/VipNFT.sol @@ -4,10 +4,7 @@ pragma solidity =0.8.19; import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract VIPNFT is ERC721 { - constructor() - // solhint-disable-next-line no-empty-blocks - ERC721("VIP", "VIP") - {} + constructor() ERC721("VIP", "VIP") {} function mint(address to, uint256 amount) external { _safeMint(to, amount); diff --git a/foundry.toml b/foundry.toml index f34bad8..49aff4d 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,6 +9,15 @@ cache_path = 'cache_forge' no_match_test = 'testCounter' gas_reports = ["*"] gas_reports_ignore = ["TestToken", "TestCounter"] +remappings = [ + "@openzeppelin/=node_modules/@openzeppelin/", + "account-abstraction/=lib/account-abstraction/contracts/", + "ds-test/=lib/forge-std/lib/ds-test/src/", + "eth-gas-reporter/=node_modules/eth-gas-reporter/", + "forge-std/=lib/forge-std/src/", + "hardhat/=node_modules/hardhat/", + "erc6551/=lib/erc6551/", +] # default optimizer = true diff --git a/package.json b/package.json index 8f51e3e..2764cf1 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "eslint-plugin-n": "^15.7.0", "eslint-plugin-promise": "^6.1.1", "hardhat": "^2.14.0", - "solhint": "^3.4.1", + "solhint": "3.6.2", "ts-node": "^10.9.1", "typechain": "^8.1.1", "typescript": "^5.0.4" diff --git a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol index 3c3a9d3..835bc17 100644 --- a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol +++ b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol @@ -62,7 +62,7 @@ contract RecoverableOpenfortAccountTest is Test { error InvalidSignatureAmount(); // keccak256("Recover(address recoveryAddress,uint64 executeAfter,uint32 guardiansRequired)"); - bytes32 RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; + bytes32 private RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index e2f556f..70c5f52 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -142,8 +142,8 @@ contract UpgradeableOpenfortAccountTest is Test { } // If not a fork, deploy entryPoint (at correct address) else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; + EntryPoint entryPointAux = new EntryPoint(); + bytes memory code = address(entryPointAux).code; address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); vm.etch(targetAddr, code); entryPoint = EntryPoint(payable(targetAddr)); diff --git a/yarn.lock b/yarn.lock index 6faf72b..6fb1cd8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4812,6 +4812,13 @@ semver@^7.0.0, semver@^7.3.4, semver@^7.3.7, semver@^7.3.8: dependencies: lru-cache "^6.0.0" +semver@^7.5.2: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -4914,10 +4921,10 @@ solc@0.7.3: semver "^5.5.0" tmp "0.0.33" -solhint@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.4.1.tgz#8ea15b21c13d1be0b53fd46d605a24d0b36a0c46" - integrity sha512-pzZn2RlZhws1XwvLPVSsxfHrwsteFf5eySOhpAytzXwKQYbTCJV6z8EevYDiSVKMpWrvbKpEtJ055CuEmzp4Xg== +solhint@3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.6.2.tgz#2b2acbec8fdc37b2c68206a71ba89c7f519943fe" + integrity sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ== dependencies: "@solidity-parser/parser" "^0.16.0" ajv "^6.12.6" @@ -4932,7 +4939,7 @@ solhint@^3.4.1: js-yaml "^4.1.0" lodash "^4.17.21" pluralize "^8.0.0" - semver "^6.3.0" + semver "^7.5.2" strip-ansi "^6.0.1" table "^6.8.1" text-table "^0.2.0" From f8b9344f9cd76a5c0bb10eda4503b6040a244e9c Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 15 Nov 2023 18:27:49 +0100 Subject: [PATCH 17/83] More optimizations --- contracts/core/BaseOpenfortAccount.sol | 19 ++++++++++--------- .../core/eip6551/EIP6551OpenfortAccount.sol | 4 +++- .../core/managed/ManagedOpenfortFactory.sol | 4 +--- .../RecoverableOpenfortAccount.sol | 5 +++-- contracts/mock/SimpleNFT.sol | 2 +- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index d05de21..e875e67 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -154,8 +154,8 @@ abstract contract BaseOpenfortAccount is // Check if it is a masterSessionKey (no whitelist applies) if (sessionKey.masterSessionKey) return true; - - for (uint256 i = 0; i < toContracts.length;) { + uint256 i; + for (i; i < toContracts.length;) { if (toContracts[i] == address(this)) { return false; } // Only masterSessionKey can reenter @@ -163,7 +163,7 @@ abstract contract BaseOpenfortAccount is return false; } // One contract's not in the sessionKey's whitelist (if any) unchecked { - ++i; // gas optimization + ++i; } } return true; @@ -217,10 +217,11 @@ abstract contract BaseOpenfortAccount is if (_target.length > 9 || _target.length != _calldata.length || _target.length != _value.length) { revert InvalidParameterLength(); } - for (uint256 i = 0; i < _target.length;) { + uint256 i; + for (i; i < _target.length;) { _call(_target[i], _value[i], _calldata[i]); unchecked { - ++i; // gas optimization + ++i; } } } @@ -263,7 +264,7 @@ abstract contract BaseOpenfortAccount is override returns (uint256 validationData) { - // require(entryPoint().getUserOpHash(userOp) == userOpHash, "MEEECK!"); + require(entryPoint().getUserOpHash(userOp) == userOpHash, "Calculated userOpHash doesn't match"); bytes32 hash = userOpHash.toEthSignedMessageHash(); address signer = hash.recover(userOp.signature); @@ -296,14 +297,14 @@ abstract contract BaseOpenfortAccount is _requireFromEntryPointOrOwner(); require(_whitelist.length < 11, "Whitelist too big"); - uint256 i = 0; + uint256 i; for (i; i < _whitelist.length;) { sessionKeys[_key].whitelist[_whitelist[i]] = true; unchecked { - ++i; // gas optimization + ++i; } } - if (i > 0) { + if (i != 0) { // If there was some whitelisting, it is not a masterSessionKey sessionKeys[_key].whitelising = true; sessionKeys[_key].masterSessionKey = false; diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index e216443..b52aa58 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -30,6 +30,8 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 uint256 public state; + error OperationNotAllowed(); + event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); receive() external payable override(BaseOpenfortAccount, IERC6551Account) {} @@ -89,7 +91,7 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 override returns (bytes memory _result) { - require(_operation == 0, "Only call operations are supported"); + if (_operation != 0) revert OperationNotAllowed(); ++state; bool success; // solhint-disable-next-line avoid-low-level-calls diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index 55f58f8..402781f 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -40,9 +40,7 @@ contract ManagedOpenfortFactory is IBaseOpenfortFactory, UpgradeableBeacon { bytes32 salt = keccak256(abi.encode(_admin, _nonce)); account = getAddressWithNonce(_admin, _nonce); - if (account.code.length > 0) { - return account; - } + if (account.code.length != 0) return account; emit AccountCreated(account, _admin); account = address( diff --git a/contracts/core/recoverable/RecoverableOpenfortAccount.sol b/contracts/core/recoverable/RecoverableOpenfortAccount.sol index 33ae706..39f0cb9 100644 --- a/contracts/core/recoverable/RecoverableOpenfortAccount.sol +++ b/contracts/core/recoverable/RecoverableOpenfortAccount.sol @@ -54,7 +54,7 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradea RecoveryConfig public recoveryDetails; // keccak256("Recover(address recoveryAddress,uint64 executeAfter,uint32 guardiansRequired)"); - bytes32 public constant RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; + bytes32 private constant RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); event Locked(bool isLocked); @@ -208,7 +208,8 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradea */ function getGuardians() external view returns (address[] memory) { address[] memory guardians = new address[](guardiansConfig.guardians.length); - for (uint256 i = 0; i < guardiansConfig.guardians.length;) { + uint256 i; + for (i; i < guardiansConfig.guardians.length;) { guardians[i] = guardiansConfig.guardians[i]; unchecked { ++i; // gas optimization diff --git a/contracts/mock/SimpleNFT.sol b/contracts/mock/SimpleNFT.sol index 5f08f7c..6dd0a36 100644 --- a/contracts/mock/SimpleNFT.sol +++ b/contracts/mock/SimpleNFT.sol @@ -11,6 +11,6 @@ contract SimpleNFT is ERC721 { // Anyone can mint an NFT for anyone function mint(address _to) public { - _safeMint(_to, tokenId++); + _safeMint(_to, ++tokenId); } } From eab4a957c0d4dd2f6ac57ad57dbef68481e35dfd Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Thu, 16 Nov 2023 13:16:40 +0100 Subject: [PATCH 18/83] Naming the mappings --- contracts/core/BaseOpenfortAccount.sol | 4 ++-- contracts/core/recoverable/RecoverableOpenfortAccount.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index e875e67..a7f94c1 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -53,11 +53,11 @@ abstract contract BaseOpenfortAccount is uint48 limit; bool masterSessionKey; bool whitelising; - mapping(address => bool) whitelist; + mapping(address contractAddress => bool allowed) whitelist; address registrarAddress; } - mapping(address => SessionKeyStruct) public sessionKeys; + mapping(address sessionKey => SessionKeyStruct sessionKeyData) public sessionKeys; event AccountCreated(address indexed creator); event SessionKeyRegistered(address indexed key); diff --git a/contracts/core/recoverable/RecoverableOpenfortAccount.sol b/contracts/core/recoverable/RecoverableOpenfortAccount.sol index 39f0cb9..08ed6af 100644 --- a/contracts/core/recoverable/RecoverableOpenfortAccount.sol +++ b/contracts/core/recoverable/RecoverableOpenfortAccount.sol @@ -40,7 +40,7 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradea struct GuardiansConfig { address[] guardians; // list of guardian addresses - mapping(address => GuardianInfo) info; // info about guardians + mapping(address guardianAddress => GuardianInfo guardianInfo) info; // info about guardians uint256 lock; // Lock's release timestamp } From 075df6734ac7398f07eeaa2cc94d0e1b307ac023 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Thu, 16 Nov 2023 13:38:10 +0100 Subject: [PATCH 19/83] Adding custom error --- contracts/core/recoverable/RecoverableOpenfortAccount.sol | 4 +--- .../core/recoverable/RecoverableOpenfortAccountTest.t.sol | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/core/recoverable/RecoverableOpenfortAccount.sol b/contracts/core/recoverable/RecoverableOpenfortAccount.sol index 08ed6af..3815a8b 100644 --- a/contracts/core/recoverable/RecoverableOpenfortAccount.sol +++ b/contracts/core/recoverable/RecoverableOpenfortAccount.sol @@ -380,7 +380,7 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradea */ function completeRecovery(bytes[] calldata _signatures) external { _requireRecovery(true); - require(uint64(block.timestamp) > recoveryDetails.executeAfter, "Ongoing recovery period"); + if (recoveryDetails.executeAfter > uint64(block.timestamp)) revert OngoingRecovery(); require(recoveryDetails.guardiansRequired > 0, "No guardians set on wallet"); if (recoveryDetails.guardiansRequired != _signatures.length) revert InvalidSignatureAmount(); @@ -390,8 +390,6 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradea address recoveryOwner = recoveryDetails.recoveryAddress; delete recoveryDetails; - // End sessions here? - _transferOwnership(recoveryOwner); _setLock(0); diff --git a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol index 835bc17..67a85b4 100644 --- a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol +++ b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol @@ -1964,7 +1964,7 @@ contract RecoverableOpenfortAccountTest is Test { assertEq(recoverableOpenfortAccount.isLocked(), true); // The recovery time period has not passed. The user should wait to recover. - vm.expectRevert("Ongoing recovery period"); + vm.expectRevert(OngoingRecovery.selector); bytes[] memory signatures = new bytes[](1); recoverableOpenfortAccount.completeRecovery(signatures); From 3ccd7c3ffdf027eee83ee7c34b060f953401098b Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 17 Nov 2023 12:39:14 +0100 Subject: [PATCH 20/83] Minor adjustments --- .vscode/settings.json | 5 +++-- contracts/core/eip6551/EIP6551OpenfortAccount.sol | 5 ++--- .../core/recoverable/RecoverableOpenfortAccount.sol | 1 - slither.config.json | 9 ++++++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 85fb756..90023f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "solidity.compileUsingRemoteVersion": "v0.8.19+commit.7dd6d404", - "solidity.defaultCompiler": "remote", - "solidity.compileUsingLocalVersion": "/Users/eloimanuel/Projects/Openfort/openfort-contracts/soljson-v0.8.19+commit.7dd6d404.js" + "solidity.defaultCompiler": "localFile", + "solidity.compileUsingLocalVersion": "v0.8.19+commit.7dd6d404", + "solidity.packageDefaultDependenciesDirectory": "node_modules/, lib/" } \ No newline at end of file diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index b52aa58..561973d 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -50,7 +50,7 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 } emit EntryPointUpdated(entrypointContract, _entrypoint); entrypointContract = _entrypoint; - __EIP712_init("Openfort", "0.4"); + __EIP712_init("Openfort", "0.5"); state = 1; } @@ -94,7 +94,6 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 if (_operation != 0) revert OperationNotAllowed(); ++state; bool success; - // solhint-disable-next-line avoid-low-level-calls (success, _result) = _target.call{value: _value}(_data); require(success, string(_result)); return _result; @@ -103,7 +102,7 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 /** * Execute a transaction (called directly from owner, or by entryPoint) */ - function execute(address dest, uint256 value, bytes calldata func) public override { + function execute(address dest, uint256 value, bytes calldata func) public override returns (bytes memory _result) { ++state; super.execute(dest, value, func); } diff --git a/contracts/core/recoverable/RecoverableOpenfortAccount.sol b/contracts/core/recoverable/RecoverableOpenfortAccount.sol index 3815a8b..bf523ba 100644 --- a/contracts/core/recoverable/RecoverableOpenfortAccount.sol +++ b/contracts/core/recoverable/RecoverableOpenfortAccount.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -// Base account contract to inherit from and EntryPoint interface import { Ownable2StepUpgradeable, OwnableUpgradeable diff --git a/slither.config.json b/slither.config.json index 6583cde..89e7623 100644 --- a/slither.config.json +++ b/slither.config.json @@ -1,9 +1,12 @@ { "solc_remaps": [ - "ds-test/=lib/ds-test/src/", - "forge-std/=lib/forge-std/src/", "@openzeppelin/=node_modules/@openzeppelin/", - "account-abstraction/=lib/account-abstraction/contracts/" + "account-abstraction/=lib/account-abstraction/contracts/", + "ds-test/=lib/forge-std/lib/ds-test/src/", + "eth-gas-reporter/=node_modules/eth-gas-reporter/", + "forge-std/=lib/forge-std/src/", + "hardhat/=node_modules/hardhat/", + "erc6551/=lib/erc6551/" ], "filter_paths": "(lib/|script/|test/|openzeppelin)", "exclude_informational": false, From 4732a2b7a8d76a5681ba8a7768cf8f5fa9ce0e88 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 17 Nov 2023 16:31:48 +0100 Subject: [PATCH 21/83] Improving local vscode config --- .vscode/settings.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 90023f6..061a189 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,16 @@ "solidity.compileUsingRemoteVersion": "v0.8.19+commit.7dd6d404", "solidity.defaultCompiler": "localFile", "solidity.compileUsingLocalVersion": "v0.8.19+commit.7dd6d404", - "solidity.packageDefaultDependenciesDirectory": "node_modules/, lib/" + "solidity.packageDefaultDependenciesDirectory": "node_modules/, lib/", + "solidity.formatter": "forge", + "solidity.packageDefaultDependenciesContractsDirectory": "node_modules/, lib/", + "solidity.remappings": [ + "@openzeppelin/=node_modules/@openzeppelin/", + "account-abstraction/=lib/account-abstraction/contracts/", + "ds-test/=lib/forge-std/lib/ds-test/src/", + "eth-gas-reporter/=node_modules/eth-gas-reporter/", + "forge-std/=lib/forge-std/src/", + "hardhat/=node_modules/hardhat/", + "erc6551/=lib/erc6551/", + ] } \ No newline at end of file From d0d12734c878504b0528374d82213310b4f8e613 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 17 Nov 2023 17:53:38 +0100 Subject: [PATCH 22/83] More improvements --- contracts/core/BaseOpenfortAccount.sol | 12 ++-- .../core/eip6551/EIP6551OpenfortAccount.sol | 21 +++---- .../core/managed/OpenfortBeaconProxy.sol | 2 +- contracts/mock/MockERC1155.sol | 12 ++++ contracts/mock/{USDC.sol => MockERC20.sol} | 4 +- contracts/mock/MockERC721.sol | 12 ++++ ...t.sol => MockV2ManagedOpenfortAccount.sol} | 8 +-- ...l => MockV2UpgradeableOpenfortAccount.sol} | 12 ++-- contracts/mock/Rewards.sol | 12 ---- contracts/mock/VipNFT.sol | 12 ---- script/deployEIP6551.sol | 10 +-- script/deployMock.sol | 8 +-- .../eip6551/EIP6551OpenfortAccountTest.t.sol | 62 ++++++++++++------- .../eip6551/EIP6551OpenfortBenchmark.t.sol | 50 +++++++-------- .../managed/ManagedOpenfortAccountTest.t.sol | 39 ++++++------ .../RecoverableOpenfortAccountTest.t.sol | 25 -------- .../UpgradeableOpenfortAccountTest.t.sol | 33 +++++----- 17 files changed, 156 insertions(+), 178 deletions(-) create mode 100644 contracts/mock/MockERC1155.sol rename contracts/mock/{USDC.sol => MockERC20.sol} (74%) create mode 100644 contracts/mock/MockERC721.sol rename contracts/mock/{MockedV2ManagedOpenfortAccount.sol => MockV2ManagedOpenfortAccount.sol} (84%) rename contracts/mock/{MockedV2UpgradeableOpenfortAccount.sol => MockV2UpgradeableOpenfortAccount.sol} (78%) delete mode 100644 contracts/mock/Rewards.sol delete mode 100644 contracts/mock/VipNFT.sol diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index a7f94c1..03111cc 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -105,7 +105,7 @@ abstract contract BaseOpenfortAccount is /* * @notice Return whether a sessionKey is valid. */ - function isValidSessionKey(address _sessionKey, bytes calldata callData) public returns (bool) { + function isValidSessionKey(address _sessionKey, bytes calldata _callData) public returns (bool) { SessionKeyStruct storage sessionKey = sessionKeys[_sessionKey]; // If not owner and the session key is revoked, return false if (sessionKey.validUntil == 0) return false; @@ -116,7 +116,7 @@ abstract contract BaseOpenfortAccount is // If the signer is a session key that is still valid // Let's first get the selector of the function that the caller is using bytes4 funcSelector = - callData[0] | (bytes4(callData[1]) >> 8) | (bytes4(callData[2]) >> 16) | (bytes4(callData[3]) >> 24); + _callData[0] | (bytes4(_callData[1]) >> 8) | (bytes4(_callData[2]) >> 16) | (bytes4(_callData[3]) >> 24); if (funcSelector == EXECUTE_SELECTOR) { // Limit of transactions per sessionKey reached @@ -133,7 +133,7 @@ abstract contract BaseOpenfortAccount is // If it is not a masterSessionKey, let's check for whitelisting and reentrancy address toContract; - (toContract,,) = abi.decode(callData[4:], (address, uint256, bytes)); + (toContract,,) = abi.decode(_callData[4:], (address, uint256, bytes)); if (toContract == address(this)) { return false; } // Only masterSessionKey can reenter @@ -145,7 +145,7 @@ abstract contract BaseOpenfortAccount is return false; // All other cases, deny } else if (funcSelector == EXECUTEBATCH_SELECTOR) { - (address[] memory toContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); + (address[] memory toContracts,,) = abi.decode(_callData[4:], (address[], uint256[], bytes[])); // Check if limit of transactions per sessionKey reached if (sessionKey.limit < toContracts.length || toContracts.length > 9) return false; unchecked { @@ -334,8 +334,4 @@ abstract contract BaseOpenfortAccount is emit SessionKeyRevoked(_key); } } - - function version() external pure virtual returns (uint256) { - return 1; - } } diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index 561973d..80ae206 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -8,7 +8,6 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC6551Account} from "erc6551/src/interfaces/IERC6551Account.sol"; import {IERC6551Executable} from "erc6551/src/interfaces/IERC6551Executable.sol"; import {ERC6551AccountLib} from "erc6551/src/lib/ERC6551AccountLib.sol"; -import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; import {BaseOpenfortAccount, IEntryPoint, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; @@ -73,13 +72,13 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 /** * @dev {See IERC6551Account-isValidSigner} */ - function isValidSigner(address signer, bytes calldata) external view override returns (bytes4) { - if (_isValidSigner(signer)) return IERC6551Account.isValidSigner.selector; + function isValidSigner(address _signer, bytes calldata) external view override returns (bytes4) { + if (_isValidSigner(_signer)) return IERC6551Account.isValidSigner.selector; return bytes4(0); } - function _isValidSigner(address signer) internal view returns (bool) { - return signer == owner(); + function _isValidSigner(address _signer) internal view returns (bool) { + return _signer == owner(); } /** @@ -102,9 +101,9 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 /** * Execute a transaction (called directly from owner, or by entryPoint) */ - function execute(address dest, uint256 value, bytes calldata func) public override returns (bytes memory _result) { + function execute(address _dest, uint256 _value, bytes calldata _func) public override { ++state; - super.execute(dest, value, func); + super.execute(_dest, _value, _func); } /** @@ -138,11 +137,11 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 return IEntryPoint(entrypointContract); } - function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { + function supportsInterface(bytes4 _interfaceId) public pure override returns (bool) { return ( - interfaceId == type(IERC6551Account).interfaceId || interfaceId == type(IERC6551Executable).interfaceId - || interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC721Receiver).interfaceId - || interfaceId == type(IERC165).interfaceId + _interfaceId == type(IERC6551Account).interfaceId || _interfaceId == type(IERC6551Executable).interfaceId + || _interfaceId == type(IERC1155Receiver).interfaceId || _interfaceId == type(IERC721Receiver).interfaceId + || _interfaceId == type(IERC165).interfaceId ); } } diff --git a/contracts/core/managed/OpenfortBeaconProxy.sol b/contracts/core/managed/OpenfortBeaconProxy.sol index 40d2095..57565ac 100644 --- a/contracts/core/managed/OpenfortBeaconProxy.sol +++ b/contracts/core/managed/OpenfortBeaconProxy.sol @@ -5,7 +5,7 @@ import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol" /** * @title OpenfortBeaconProxy (Non-upgradeable) - * @notice Contract to create the Beacon to determine implementation contract, which is where they will delegate all function calls. + * @notice Contract to create the beacon. It determines the implementation contract. * It inherits from: * - BeaconProxy */ diff --git a/contracts/mock/MockERC1155.sol b/contracts/mock/MockERC1155.sol new file mode 100644 index 0000000..dc5d055 --- /dev/null +++ b/contracts/mock/MockERC1155.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; + +contract MockERC1155 is ERC1155 { + constructor() ERC1155("MockERC1155") {} + + function mint(address to, uint256 tokenId, uint256 amount) external { + _mint(to, tokenId, amount, ""); + } +} diff --git a/contracts/mock/USDC.sol b/contracts/mock/MockERC20.sol similarity index 74% rename from contracts/mock/USDC.sol rename to contracts/mock/MockERC20.sol index c523585..a2a4dca 100644 --- a/contracts/mock/USDC.sol +++ b/contracts/mock/MockERC20.sol @@ -3,8 +3,8 @@ pragma solidity =0.8.19; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -contract USDC is ERC20 { - constructor() ERC20("USDC", "USDC") {} +contract MockERC20 is ERC20 { + constructor() ERC20("MockERC20", "MC20") {} function mint(address sender, uint256 amount) external { _mint(sender, amount); diff --git a/contracts/mock/MockERC721.sol b/contracts/mock/MockERC721.sol new file mode 100644 index 0000000..2d79644 --- /dev/null +++ b/contracts/mock/MockERC721.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +contract MockERC721 is ERC721 { + constructor() ERC721("MockERC721", "M721") {} + + function mint(address to, uint256 tokenId) external { + _safeMint(to, tokenId); + } +} diff --git a/contracts/mock/MockedV2ManagedOpenfortAccount.sol b/contracts/mock/MockV2ManagedOpenfortAccount.sol similarity index 84% rename from contracts/mock/MockedV2ManagedOpenfortAccount.sol rename to contracts/mock/MockV2ManagedOpenfortAccount.sol index d6e5786..eca5520 100644 --- a/contracts/mock/MockedV2ManagedOpenfortAccount.sol +++ b/contracts/mock/MockV2ManagedOpenfortAccount.sol @@ -15,7 +15,7 @@ import {BaseOpenfortAccount, IEntryPoint} from "../core/BaseOpenfortAccount.sol" * It inherits from: * - BaseOpenfortAccount */ -contract MockedV2ManagedOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable { +contract MockV2ManagedOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable { address private constant ENTRYPOINTCONTRACT = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; /* @@ -28,10 +28,6 @@ contract MockedV2ManagedOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgr _transferOwnership(_defaultAdmin); } - function version() external pure override returns (uint256) { - return 2; - } - function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { return OwnableUpgradeable.owner(); } @@ -40,6 +36,6 @@ contract MockedV2ManagedOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgr * Return the current EntryPoint */ function entryPoint() public pure override returns (IEntryPoint) { - return IEntryPoint(ENTRYPOINTCONTRACT); + return IEntryPoint(address(0)); } } diff --git a/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol similarity index 78% rename from contracts/mock/MockedV2UpgradeableOpenfortAccount.sol rename to contracts/mock/MockV2UpgradeableOpenfortAccount.sol index 3f57d8f..f3a1c71 100644 --- a/contracts/mock/MockedV2UpgradeableOpenfortAccount.sol +++ b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol @@ -11,13 +11,13 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U import {BaseOpenfortAccount, IEntryPoint} from "../core/BaseOpenfortAccount.sol"; /** - * @title MockedV2UpgradeableOpenfortAccount + * @title MockV2UpgradeableOpenfortAccount * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. * It inherits from: * - BaseOpenfortAccount * - UUPSUpgradeable */ -contract MockedV2UpgradeableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable, UUPSUpgradeable { +contract MockV2UpgradeableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable, UUPSUpgradeable { address internal entrypointContract; /* * @notice Initialize the smart contract wallet. @@ -33,10 +33,6 @@ contract MockedV2UpgradeableOpenfortAccount is BaseOpenfortAccount, Ownable2Step function _authorizeUpgrade(address) internal override onlyOwner {} - function version() external pure override returns (uint256) { - return 2; - } - function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { return OwnableUpgradeable.owner(); } @@ -44,7 +40,7 @@ contract MockedV2UpgradeableOpenfortAccount is BaseOpenfortAccount, Ownable2Step /** * Return the current EntryPoint */ - function entryPoint() public view override returns (IEntryPoint) { - return IEntryPoint(entrypointContract); + function entryPoint() public pure override returns (IEntryPoint) { + return IEntryPoint(address(0)); } } diff --git a/contracts/mock/Rewards.sol b/contracts/mock/Rewards.sol deleted file mode 100644 index ea515c6..0000000 --- a/contracts/mock/Rewards.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract Rewards is ERC20 { - constructor() ERC20("GEMS", "GEMS") {} - - function claim(uint256 amount) external { - _mint(msg.sender, amount); - } -} diff --git a/contracts/mock/VipNFT.sol b/contracts/mock/VipNFT.sol deleted file mode 100644 index 679eb4b..0000000 --- a/contracts/mock/VipNFT.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; - -contract VIPNFT is ERC721 { - constructor() ERC721("VIP", "VIP") {} - - function mint(address to, uint256 amount) external { - _safeMint(to, amount); - } -} diff --git a/script/deployEIP6551.sol b/script/deployEIP6551.sol index 70a711e..04024a2 100644 --- a/script/deployEIP6551.sol +++ b/script/deployEIP6551.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; -import {VIPNFT} from "contracts/mock/VipNFT.sol"; +import {MockERC721} from "contracts/mock/MockERC721.sol"; import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; import {IERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; @@ -12,7 +12,7 @@ contract EIP6551OpenfortDeploy is Script { address internal deployAddress = vm.addr(deployPrivKey); IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); IERC6551Registry internal erc6551Registry = IERC6551Registry((payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS")))); - VIPNFT internal testToken; + MockERC721 internal nft721; function run() public { bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); @@ -26,12 +26,12 @@ contract EIP6551OpenfortDeploy is Script { chainId := chainid() } - // deploy a new VIPNFT collection - testToken = new VIPNFT(); + // deploy a new MockERC721 collection + nft721 = new MockERC721(); // The first call should create a new account, while the second will just return the corresponding account address address account2 = - erc6551Registry.createAccount(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1); + erc6551Registry.createAccount(address(eip6551OpenfortAccount), versionSalt, chainId, address(nft721), 1); console.log("Registry at address %s has created an account at address %s", address(erc6551Registry), account2); vm.stopBroadcast(); diff --git a/script/deployMock.sol b/script/deployMock.sol index 71fb6e7..2ef75fc 100644 --- a/script/deployMock.sol +++ b/script/deployMock.sol @@ -4,8 +4,7 @@ pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; import {UpgradeableOpenfortFactory} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; -// import {USDC} from "../contracts/mock/USDC.sol"; -import {Rewards} from "../contracts/mock/Rewards.sol"; +import {MockERC20} from "../contracts/mock/MockERC20.sol"; contract DeployMock is Script { uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); @@ -16,9 +15,8 @@ contract DeployMock is Script { function run() public { vm.startBroadcast(deployPrivKey); - // USDC u = new USDC(); - Rewards r = new Rewards(); - (r); + MockERC20 mockERC20 = new MockERC20(); + (mockERC20); vm.stopBroadcast(); } diff --git a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol index 0ea9de6..63ab424 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol @@ -2,12 +2,12 @@ pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; -import {SigUtils} from "../../utils/SigUtils.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {VIPNFT} from "contracts/mock/VipNFT.sol"; -import {ERC6551Registry, IERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; +import {MockERC721} from "contracts/mock/MockERC721.sol"; import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; +import {SigUtils} from "../../utils/SigUtils.sol"; contract EIP6551OpenfortAccountTest is Test { using ECDSA for bytes32; @@ -18,7 +18,7 @@ contract EIP6551OpenfortAccountTest is Test { EIP6551OpenfortAccount public implEIP6551OpenfortAccount; bytes32 public versionSalt = vm.envBytes32("VERSION_SALT"); address public account; - VIPNFT public testToken; + MockERC721 public mockERC721; // Testing addresses address private factoryAdmin; @@ -112,7 +112,7 @@ contract EIP6551OpenfortAccountTest is Test { /** * @notice Initialize the StaticOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory + * - factoryAdmin is the deployer (and owner) of the mockNFT * - accountAdmin is the account used to deploy new static accounts * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps @@ -135,7 +135,7 @@ contract EIP6551OpenfortAccountTest is Test { if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); } - // If not a fork, deploy entryPoint (at correct address) + // If not a fork, deploy entryPoint (at the correct address) else { EntryPoint entryPoint_aux = new EntryPoint(); bytes memory code = address(entryPoint_aux).code; @@ -148,7 +148,7 @@ contract EIP6551OpenfortAccountTest is Test { if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); } - // If not a fork, deploy entryPoint (at correct address) + // If not a fork, deploy ERC6551 registry (at the correct address) else { ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); bytes memory code = address(ERC6551Registry_aux).code; @@ -157,31 +157,45 @@ contract EIP6551OpenfortAccountTest is Test { erc6551Registry = ERC6551Registry(payable(targetAddr)); } - // deploy a new VIPNFT collection - testToken = new VIPNFT(); + // deploy a new MockERC721 collection + mockERC721 = new MockERC721(); implEIP6551OpenfortAccount = new EIP6551OpenfortAccount(); address eip6551OpenfortAccountAddress = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), versionSalt, chainId, address(testToken), 1 + address(implEIP6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 ); eip6551OpenfortAccount = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress)); eip6551OpenfortAccount.initialize(address(entryPoint)); - testToken.mint(eip6551OpenfortAccountAddress, 1); + mockERC721.mint(eip6551OpenfortAccountAddress, 1); vm.stopPrank(); } /* * Test reinitialize. It should fail. - * */ function testFailReinitialize() public { eip6551OpenfortAccount.initialize(address(entryPoint)); } + /* + * Test deploy. Regular, no userOps. + */ + function testERC6551Deploy() public { + address deployedAccount = + erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), 0, block.chainid, address(0), 0); + + assertTrue(deployedAccount != address(0)); + + address predictedAccount = + erc6551Registry.account(address(implEIP6551OpenfortAccount), 0, block.chainid, address(0), 0); + + assertEq(predictedAccount, deployedAccount); + } + /* * Test initialize implementation. It should fail. */ @@ -207,7 +221,7 @@ contract EIP6551OpenfortAccountTest is Test { chainId := chainid() } address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), bytes32(0), chainId, address(testToken), 1 + address(implEIP6551OpenfortAccount), bytes32(0), chainId, address(mockERC721), 1 ); EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); @@ -225,7 +239,7 @@ contract EIP6551OpenfortAccountTest is Test { chainId := chainid() } address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), versionSalt, chainId, address(testToken), 1 + address(implEIP6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 ); EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); @@ -242,7 +256,7 @@ contract EIP6551OpenfortAccountTest is Test { chainId := chainid() } address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), versionSalt, chainId, address(testToken), 1 + address(implEIP6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 ); EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); @@ -274,7 +288,7 @@ contract EIP6551OpenfortAccountTest is Test { * Check that the owner of the eip6551 account is the owner of the NFT */ function testOwner() public { - assertEq(eip6551OpenfortAccount.owner(), testToken.ownerOf(1)); + assertEq(eip6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); assertEq(eip6551OpenfortAccount.owner(), address(eip6551OpenfortAccount)); } @@ -285,9 +299,9 @@ contract EIP6551OpenfortAccountTest is Test { function testNotOwner() public { // Burning the NFT vm.prank(address(eip6551OpenfortAccount)); - testToken.transferFrom(address(eip6551OpenfortAccount), address(1), 1); + mockERC721.transferFrom(address(eip6551OpenfortAccount), address(1), 1); - assertEq(eip6551OpenfortAccount.owner(), testToken.ownerOf(1)); + assertEq(eip6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); assertNotEq(eip6551OpenfortAccount.owner(), address(eip6551OpenfortAccount)); assertEq(eip6551OpenfortAccount.owner(), address(1)); } @@ -304,31 +318,31 @@ contract EIP6551OpenfortAccountTest is Test { // Get the counterfactual address vm.prank(factoryAdmin); address eip6551OpenfortAccountAddress2 = - erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1); + erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); // Expect that we will see an event containing the account and admin // vm.expectEmit(true, true, false, true); // emit IERC6551Registry.ERC6551AccountCreated( - // eip6551OpenfortAccountAddress2, address(eip6551OpenfortAccount), chainId, address(testToken), 1, 2 + // eip6551OpenfortAccountAddress2, address(eip6551OpenfortAccount), chainId, address(mockERC721), 1, 2 // ); // Deploy a static account to the counterfactual address vm.prank(factoryAdmin); - erc6551Registry.createAccount(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1); + erc6551Registry.createAccount(address(eip6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); assertEq( eip6551OpenfortAccountAddress2, - erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1) + erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) ); // assertNotEq( // eip6551OpenfortAccountAddress2, - // erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(testToken), 1) + // erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) // ); assertNotEq( eip6551OpenfortAccountAddress2, - erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId + 1, address(testToken), 1) + erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) ); assertNotEq( eip6551OpenfortAccountAddress2, diff --git a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol index 64954bf..0429bde 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol @@ -5,8 +5,8 @@ import {Test, console} from "lib/forge-std/src/Test.sol"; import {SigUtils} from "../../utils/SigUtils.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {VIPNFT} from "contracts/mock/VipNFT.sol"; -import {USDC} from "contracts/mock/USDC.sol"; +import {MockERC721} from "contracts/mock/MockERC721.sol"; +import {MockERC20} from "contracts/mock/MockERC20.sol"; import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; @@ -29,7 +29,7 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // uint256 public chainId; -// VIPNFT testToken; +// MockERC721 nft721; // USDC testUSDC; // // Testing addresses @@ -181,20 +181,20 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // upgradeableOpenfortAccount = UpgradeableOpenfortAccount(payable(upgradeableOpenfortAddress)); -// // deploy a new VIPNFT collection -// testToken = new VIPNFT(); +// // deploy a new MockERC721 collection +// nft721 = new MockERC721(); // implEIP6551OpenfortAccount = new EIP6551OpenfortAccount(); // erc6551Registry = new ERC6551Registry(); // address eip6551OpenfortAccountAddress = -// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 1, 1, ""); +// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(nft721), 1, 1, ""); // eip6551OpenfortAccount = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress)); // eip6551OpenfortAccount.initialize(address(entryPoint)); -// testToken.mint(accountAdmin, 1); +// nft721.mint(accountAdmin, 1); // testUSDC = new USDC(); // testUSDC.mint(accountAdmin, 100 ether); @@ -204,10 +204,10 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // upgradeableOpenfortAddressComplex = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "complex"); // upgradeableOpenfortAccountComplex = UpgradeableOpenfortAccount(payable(upgradeableOpenfortAddressComplex)); -// testToken.mint(upgradeableOpenfortAddressComplex, 2); +// nft721.mint(upgradeableOpenfortAddressComplex, 2); // eip6551OpenfortAddressComplex = -// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 2, 2, ""); +// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(nft721), 2, 2, ""); // eip6551OpenfortAccountComplex = EIP6551OpenfortAccount(payable(eip6551OpenfortAddressComplex)); // vm.stopPrank(); @@ -226,11 +226,11 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // } // /* -// * Create a 2nd EIP6551 account with testToken as the owner and initialize later +// * Create a 2nd EIP6551 account with nft721 as the owner and initialize later // */ // function test1CreateEIP6551AccountInitAfter() public { // address eip6551OpenfortAccountAddress2 = -// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(testToken), 1, 2, ""); +// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(nft721), 1, 2, ""); // EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); // eip6551OpenfortAccount2.initialize(address(entryPoint)); @@ -239,13 +239,13 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // } // /* -// * Create a 2nd EIP6551 account with testToken as the owner and initialize during creation +// * Create a 2nd EIP6551 account with nft721 as the owner and initialize during creation // */ // function test1CreateEIP6551AccountInitDuringCreation() public { // address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( // address(implEIP6551OpenfortAccount), // chainId, -// address(testToken), +// address(nft721), // 3, // 1, // abi.encodeWithSignature("initialize(address)", address(entryPoint)) @@ -268,7 +268,7 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // * Check that the owner of the eip6551 account is the owner of the NFT // */ // function test2OwnerEIP6551() public { -// assertEq(eip6551OpenfortAccount.owner(), testToken.ownerOf(1)); +// assertEq(eip6551OpenfortAccount.owner(), nft721.ownerOf(1)); // } // /* @@ -296,7 +296,7 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // assertEq(eip6551OpenfortAccount.owner(), accountAdmin); // vm.prank(accountAdmin); -// testToken.safeTransferFrom(accountAdmin, factoryAdmin, 1); +// nft721.safeTransferFrom(accountAdmin, factoryAdmin, 1); // assertEq(eip6551OpenfortAccount.owner(), factoryAdmin); // } @@ -309,7 +309,7 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // function testFailTransferOwnerEIP6551UserOp() public { // assertEq(eip6551OpenfortAccount.owner(), accountAdmin); -// address _target = address(testToken); +// address _target = address(nft721); // bytes memory _callData = // abi.encodeWithSignature("transferFrom(address,address,uint256)", accountAdmin, factoryAdmin, 1); @@ -317,7 +317,7 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // address(eip6551OpenfortAccount), // accountAdminPKey, // bytes(""), -// address(testToken), +// address(nft721), // 0, // abi.encodeWithSignature("execute(address,uint256,bytes)", _target, 0, _callData) // ); @@ -532,7 +532,7 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT // assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); -// assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); +// assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); // vm.prank(accountAdmin); // upgradeableOpenfortAccountComplex.transferOwnership(factoryAdmin); @@ -545,7 +545,7 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // assertEq(upgradeableOpenfortAccountComplex.owner(), factoryAdmin); // assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); -// assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); +// assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); // } // /* @@ -557,17 +557,17 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); // // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT // assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); -// assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); +// assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); // vm.prank(accountAdmin); // upgradeableOpenfortAccountComplex.execute( -// address(testToken), +// address(nft721), // 0, // abi.encodeWithSignature( // "transferFrom(address,address,uint256)", upgradeableOpenfortAddressComplex, factoryAdmin, 2 // ) // ); -// assertEq(testToken.ownerOf(2), factoryAdmin); +// assertEq(nft721.ownerOf(2), factoryAdmin); // assertEq(eip6551OpenfortAccountComplex.owner(), factoryAdmin); // } @@ -579,13 +579,13 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); // // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT // assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); -// assertEq(testToken.ownerOf(2), upgradeableOpenfortAddressComplex); +// assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); // UserOperation[] memory userOp = _setupUserOpExecute( // upgradeableOpenfortAddressComplex, // accountAdminPKey, // bytes(""), -// address(testToken), +// address(nft721), // 0, // abi.encodeWithSignature( // "safeTransferFrom(address,address,uint256)", upgradeableOpenfortAddressComplex, factoryAdmin, 2 @@ -597,7 +597,7 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // entryPoint.simulateValidation(userOp[0]); // entryPoint.handleOps(userOp, beneficiary); -// assertEq(testToken.ownerOf(2), factoryAdmin); +// assertEq(nft721.ownerOf(2), factoryAdmin); // assertEq(eip6551OpenfortAccountComplex.owner(), factoryAdmin); // } // } diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index bca7926..d0487a4 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -5,11 +5,11 @@ import {Test, console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {TestToken} from "account-abstraction/test/TestToken.sol"; +import {MockERC20} from "contracts/mock/MockERC20.sol"; import {ManagedOpenfortAccount} from "contracts/core/managed/ManagedOpenfortAccount.sol"; import {ManagedOpenfortFactory} from "contracts/core/managed/ManagedOpenfortFactory.sol"; import {OpenfortBeaconProxy} from "contracts/core/managed/OpenfortBeaconProxy.sol"; -import {MockedV2ManagedOpenfortAccount} from "contracts/mock/MockedV2ManagedOpenfortAccount.sol"; +import {MockV2ManagedOpenfortAccount} from "contracts/mock/MockV2ManagedOpenfortAccount.sol"; contract ManagedOpenfortAccountTest is Test { using ECDSA for bytes32; @@ -20,7 +20,7 @@ contract ManagedOpenfortAccountTest is Test { ManagedOpenfortFactory public managedOpenfortFactory; address public account; TestCounter public testCounter; - TestToken public testToken; + MockERC20 public mockERC20; // Testing addresses address private factoryAdmin; @@ -152,8 +152,8 @@ contract ManagedOpenfortAccountTest is Test { account = managedOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); // deploy a new TestCounter testCounter = new TestCounter(); - // deploy a new TestToken (ERC20) - testToken = new TestToken(); + // deploy a new MockERC20 (ERC20) + mockERC20 = new MockERC20(); vm.stopPrank(); } @@ -913,21 +913,21 @@ contract ManagedOpenfortAccountTest is Test { } /* - * Test an account with testToken instead of TestCount. + * Test an account with mockERC20 instead of TestCount. */ function testMintTokenAccount() public { // Verify that the totalSupply is stil 0 - assertEq(testToken.totalSupply(), 0); + assertEq(mockERC20.totalSupply(), 0); // Mint 1 to beneficiary - testToken.mint(beneficiary, 1); - assertEq(testToken.totalSupply(), 1); + mockERC20.mint(beneficiary, 1); + assertEq(mockERC20.totalSupply(), 1); UserOperation[] memory userOp = _setupUserOpExecute( account, accountAdminPKey, bytes(""), - address(testToken), + address(mockERC20), 0, abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) ); @@ -938,7 +938,7 @@ contract ManagedOpenfortAccountTest is Test { entryPoint.handleOps(userOp, beneficiary); // Verify that the totalSupply has increased - assertEq(testToken.totalSupply(), 2); + assertEq(mockERC20.totalSupply(), 2); } /* @@ -1020,7 +1020,8 @@ contract ManagedOpenfortAccountTest is Test { vm.prank(factoryAdmin); address payable accountOld = payable(managedOpenfortFactory.createAccountWithNonce(accountAdmin, "2")); ManagedOpenfortAccount managedAccount = ManagedOpenfortAccount(accountOld); - assertEq(managedAccount.version(), 1); + assertEq(managedAccount.owner(), accountAdmin); + assertEq(address(managedAccount.entryPoint()), address(entryPoint)); OpenfortBeaconProxy p = OpenfortBeaconProxy(payable(account)); // Printing account address and the implementation address @@ -1028,7 +1029,7 @@ contract ManagedOpenfortAccountTest is Test { console.log(p.implementation()); // Deploy the new implementation - MockedV2ManagedOpenfortAccount newImplementation = new MockedV2ManagedOpenfortAccount(); + MockV2ManagedOpenfortAccount newImplementation = new MockV2ManagedOpenfortAccount(); address newImplementationAddress = address(newImplementation); vm.expectRevert("Ownable: caller is not the owner"); @@ -1038,16 +1039,18 @@ contract ManagedOpenfortAccountTest is Test { managedOpenfortFactory.upgradeTo(newImplementationAddress); assertEq(managedOpenfortFactory.accountImplementation(), newImplementationAddress); - assertEq(managedOpenfortFactory.implementation(), newImplementationAddress); //redundant view call for now (due to factory being the Beacon now) + //redundant view call for now (due to factory being the Beacon now) + assertEq(managedOpenfortFactory.implementation(), newImplementationAddress); - // Notice that, even though we bind the address to the old implementation, version() now returns 2 - assertEq(managedAccount.version(), 2); + // Notice that, even though we bind the address to the old implementation, entryPoint() is now 0 + assertEq(address(managedAccount.entryPoint()), address(0)); - // Same for new accounts. From now on, they have the new version. + // Same for new accounts. From now on, they have the new owner(). vm.prank(factoryAdmin); address payable account3 = payable(managedOpenfortFactory.createAccountWithNonce(accountAdmin, "3")); ManagedOpenfortAccount managedAccount3 = ManagedOpenfortAccount(account3); - managedAccount3.version(); + + assertEq(address(managedAccount3.entryPoint()), address(0)); // Printing account address and the implementation address. Impl address should have changed console.log(account); diff --git a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol index 67a85b4..24c8616 100644 --- a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol +++ b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol @@ -1021,31 +1021,6 @@ contract RecoverableOpenfortAccountTest is Test { assertEq(testCounter.counters(account), 0); } - // /* - // * Create an account and upgrade its implementation - // */ - // function testUpgradeAccount() public { - // assertEq(RecoverableOpenfortAccount(payable(account)).version(), 1); - // MockedV2RecoverableOpenfortAccount newAccountImplementation = new MockedV2RecoverableOpenfortAccount(); - // OpenfortUpgradeableProxy p = OpenfortUpgradeableProxy(payable(account)); - // // Printing account address and the implementation address - // console.log(account); - // console.log(p.implementation()); - - // vm.expectRevert("Ownable: caller is not the owner"); - // RecoverableOpenfortAccount(payable(account)).upgradeTo(address(newAccountImplementation)); - - // vm.prank(accountAdmin); - // RecoverableOpenfortAccount(payable(account)).upgradeTo(address(newAccountImplementation)); - - // // Notice that, even though we bind the address to the old implementation, version() now returns 2 - // assertEq(RecoverableOpenfortAccount(payable(account)).version(), 2); - - // // Printing account address and the implementation address. Impl address should have changed - // console.log(account); - // console.log(p.implementation()); - // } - /* * 1- Deploy a factory using the old EntryPoint to create an account. * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 70c5f52..1ea1f4e 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -4,13 +4,13 @@ pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; -import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {TestToken} from "account-abstraction/test/TestToken.sol"; +import {MockERC20} from "contracts/mock/MockERC20.sol"; import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {OpenfortUpgradeableProxy} from "contracts/core/upgradeable/OpenfortUpgradeableProxy.sol"; -import {MockedV2UpgradeableOpenfortAccount} from "contracts/mock/MockedV2UpgradeableOpenfortAccount.sol"; +import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; contract UpgradeableOpenfortAccountTest is Test { using ECDSA for bytes32; @@ -20,7 +20,7 @@ contract UpgradeableOpenfortAccountTest is Test { UpgradeableOpenfortFactory public upgradeableOpenfortFactory; address public account; TestCounter public testCounter; - TestToken public testToken; + MockERC20 public mockERC20; // Testing addresses address private factoryAdmin; @@ -159,8 +159,8 @@ contract UpgradeableOpenfortAccountTest is Test { // deploy a new TestCounter testCounter = new TestCounter(); - // deploy a new TestToken (ERC20) - testToken = new TestToken(); + // deploy a new MockERC20 (ERC20) + mockERC20 = new MockERC20(); vm.stopPrank(); } @@ -942,21 +942,21 @@ contract UpgradeableOpenfortAccountTest is Test { } /* - * Test an account with testToken instead of TestCount. + * Test an account with mockERC20 instead of TestCount. */ function testMintTokenAccount() public { // Verify that the totalSupply is stil 0 - assertEq(testToken.totalSupply(), 0); + assertEq(mockERC20.totalSupply(), 0); // Mint 1 to beneficiary - testToken.mint(beneficiary, 1); - assertEq(testToken.totalSupply(), 1); + mockERC20.mint(beneficiary, 1); + assertEq(mockERC20.totalSupply(), 1); UserOperation[] memory userOp = _setupUserOpExecute( account, accountAdminPKey, bytes(""), - address(testToken), + address(mockERC20), 0, abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) ); @@ -967,7 +967,7 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint.handleOps(userOp, beneficiary); // Verify that the totalSupply has increased - assertEq(testToken.totalSupply(), 2); + assertEq(mockERC20.totalSupply(), 2); } /* @@ -1026,8 +1026,9 @@ contract UpgradeableOpenfortAccountTest is Test { * Create an account and upgrade its implementation */ function testUpgradeAccount() public { - assertEq(UpgradeableOpenfortAccount(payable(account)).version(), 1); - MockedV2UpgradeableOpenfortAccount newAccountImplementation = new MockedV2UpgradeableOpenfortAccount(); + assertEq(UpgradeableOpenfortAccount(payable(account)).owner(), address(accountAdmin)); + assertEq(address(UpgradeableOpenfortAccount(payable(account)).entryPoint()), address(entryPoint)); + MockV2UpgradeableOpenfortAccount newAccountImplementation = new MockV2UpgradeableOpenfortAccount(); OpenfortUpgradeableProxy p = OpenfortUpgradeableProxy(payable(account)); // Printing account address and the implementation address console.log(account); @@ -1039,8 +1040,8 @@ contract UpgradeableOpenfortAccountTest is Test { vm.prank(accountAdmin); UpgradeableOpenfortAccount(payable(account)).upgradeTo(address(newAccountImplementation)); - // Notice that, even though we bind the address to the old implementation, version() now returns 2 - assertEq(UpgradeableOpenfortAccount(payable(account)).version(), 2); + // Notice that, even though we bind the address to the old implementation, entryPoint() is now 0 + assertEq(address(UpgradeableOpenfortAccount(payable(account)).entryPoint()), address(0)); // Printing account address and the implementation address. Impl address should have changed console.log(account); From 38f2e27b15979ca0c9f8c0653e199d20ed5d3e31 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 17 Nov 2023 18:48:29 +0100 Subject: [PATCH 23/83] Towards 2step creation (allow multple guardians) --- contracts/core/BaseOpenfortAccount.sol | 4 +- .../core/eip6551/EIP6551OpenfortAccount.sol | 5 - .../RecoverableOpenfortFactory.sol | 37 +- gas_reports/2023-11-17_18:19.gas.out | 478 ++++++++++++++++++ gas_reports/2023-11-17_18:19.snap.out | 155 ++++++ .../UpgradeableOpenfortAccountTest.t.sol | 3 + 6 files changed, 642 insertions(+), 40 deletions(-) create mode 100644 gas_reports/2023-11-17_18:19.gas.out create mode 100644 gas_reports/2023-11-17_18:19.snap.out diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/BaseOpenfortAccount.sol index 03111cc..39e9e7a 100644 --- a/contracts/core/BaseOpenfortAccount.sol +++ b/contracts/core/BaseOpenfortAccount.sol @@ -59,7 +59,7 @@ abstract contract BaseOpenfortAccount is mapping(address sessionKey => SessionKeyStruct sessionKeyData) public sessionKeys; - event AccountCreated(address indexed creator); + event AccountImplementationDeployed(address indexed creator); event SessionKeyRegistered(address indexed key); event SessionKeyRevoked(address indexed key); @@ -71,7 +71,7 @@ abstract contract BaseOpenfortAccount is receive() external payable virtual {} constructor() { - emit AccountCreated(msg.sender); + emit AccountImplementationDeployed(msg.sender); _disableInitializers(); } diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index 80ae206..24f6551 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -35,11 +35,6 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 receive() external payable override(BaseOpenfortAccount, IERC6551Account) {} - constructor() { - emit AccountCreated(msg.sender); - _disableInitializers(); - } - /* * @notice Initialize the smart contract wallet. */ diff --git a/contracts/core/recoverable/RecoverableOpenfortFactory.sol b/contracts/core/recoverable/RecoverableOpenfortFactory.sol index bf8c100..fa14f0b 100644 --- a/contracts/core/recoverable/RecoverableOpenfortFactory.sol +++ b/contracts/core/recoverable/RecoverableOpenfortFactory.sol @@ -62,21 +62,9 @@ contract RecoverableOpenfortFactory is IBaseOpenfortFactory { } emit AccountCreated(account, _admin); - account = address( - new OpenfortRecoverableProxy{salt: salt}( - accountImplementation, - abi.encodeCall( - RecoverableOpenfortAccount.initialize, - ( - _admin, - entrypointContract, - recoveryPeriod, - securityPeriod, - securityWindow, - lockPeriod, - openfortGuardian) - ) - ) + account = address(new OpenfortRecoverableProxy{salt: salt}(accountImplementation, "")); + RecoverableOpenfortAccount(payable(account)).initialize( + _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, openfortGuardian ); } @@ -88,24 +76,7 @@ contract RecoverableOpenfortFactory is IBaseOpenfortFactory { return Create2.computeAddress( salt, keccak256( - abi.encodePacked( - type(OpenfortRecoverableProxy).creationCode, - abi.encode( - accountImplementation, - abi.encodeCall( - RecoverableOpenfortAccount.initialize, - ( - _admin, - entrypointContract, - recoveryPeriod, - securityPeriod, - securityWindow, - lockPeriod, - openfortGuardian - ) - ) - ) - ) + abi.encodePacked(type(OpenfortRecoverableProxy).creationCode, abi.encode(accountImplementation, "")) ) ); } diff --git a/gas_reports/2023-11-17_18:19.gas.out b/gas_reports/2023-11-17_18:19.gas.out new file mode 100644 index 0000000..3be02ba --- /dev/null +++ b/gas_reports/2023-11-17_18:19.gas.out @@ -0,0 +1,478 @@ + +Running 1 test for test/foundry/4337/Specific4337Tests.t.sol:Specific4337Tests +[PASS] testToken() (gas: 2404) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.72ms + +Running 27 tests for test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol:ManagedOpenfortAccountTest +[PASS] testChangeOwnershipAndCountDirect() (gas: 74451) +[PASS] testChangeOwnershipAndCountEntryPoint() (gas: 251044) +[PASS] testCreateAccountViaEntryPoint() (gas: 403487) +[PASS] testCreateAccountWithNonceViaFactory() (gas: 301572) +[PASS] testFailIncrementCounterViaSessionKeyNotregistered() (gas: 220299) +[PASS] testFailIncrementCounterViaSessionKeyReachLimit() (gas: 374924) +[PASS] testFailIncrementCounterViaSessionKeyReachLimitBatching() (gas: 270326) +[PASS] testFailIncrementCounterViaSessionKeyRevoked() (gas: 81832) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingBatch() (gas: 328842) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() (gas: 292889) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingTooBig() (gas: 34737) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() (gas: 283942) +[PASS] testFailRegisterSessionKeyViaEntrypoint2ndKey() (gas: 479693) +[PASS] testFailRevokeSessionKeyInvalidUser() (gas: 84376) +[PASS] testIncrementCounterDirect() (gas: 53633) +[PASS] testIncrementCounterViaEntrypoint() (gas: 251465) +[PASS] testIncrementCounterViaEntrypointBatching() (gas: 264050) +[PASS] testIncrementCounterViaSessionKey() (gas: 293328) +[PASS] testIncrementCounterViaSessionKeyExpired() (gas: 259183) +[PASS] testIncrementCounterViaSessionKeyWhitelisting() (gas: 316800) +[PASS] testIncrementCounterViaSessionKeyWhitelistingBatch() (gas: 341790) +[PASS] testMintTokenAccount() (gas: 280183) +[PASS] testReceiveNativeToken() (gas: 27528) +[PASS] testRegisterSessionKeyViaEntrypoint() (gas: 397877) +[PASS] testSimulateValidation() (gas: 389322) +[PASS] testTransferOutNativeToken() (gas: 165373) +[PASS] testUpgradeTo() (gas: 2641914) +Test result: ok. 27 passed; 0 failed; 0 skipped; finished in 25.25ms + +Running 11 tests for test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol:EIP6551OpenfortAccountTest +[PASS] testCreate2ndAcc() (gas: 208754) +[PASS] testCreateAccInitializer() (gas: 22868) +[PASS] testCreateAccountWithNonceViaRegistry() (gas: 97697) +[PASS] testERC6551Deploy() (gas: 81959) +[PASS] testFailCreateAccInitializerNoReinit() (gas: 23223) +[PASS] testFailInitializeImplementation() (gas: 10192) +[PASS] testFailReinitialize() (gas: 12874) +[PASS] testGetDeposit() (gas: 69509) +[PASS] testImplementationNoEntryPointAddr() (gas: 7830) +[PASS] testNotOwner() (gas: 55688) +[PASS] testOwner() (gas: 20505) +Test result: ok. 11 passed; 0 failed; 0 skipped; finished in 26.33ms + +Running 31 tests for test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol:UpgradeableOpenfortAccountTest +[PASS] testChangeOwnershipAndCountDirect() (gas: 73206) +[PASS] testChangeOwnershipAndCountEntryPoint() (gas: 247729) +[PASS] testCreateAccountViaEntryPoint() (gas: 384537) +[PASS] testCreateAccountWithNonceViaFactory() (gas: 282519) +[PASS] testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() (gas: 312280) +[PASS] testFailIncrementCounterViaSessionKeyNotregistered() (gas: 214516) +[PASS] testFailIncrementCounterViaSessionKeyReachLimit() (gas: 369638) +[PASS] testFailIncrementCounterViaSessionKeyReachLimitBatching() (gas: 266411) +[PASS] testFailIncrementCounterViaSessionKeyRevoked() (gas: 78150) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() (gas: 289083) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingTooBig() (gas: 31605) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() (gas: 280116) +[PASS] testFailIsValidSignature() (gas: 53481) +[PASS] testFailIsValidSignatureMessage() (gas: 53571) +[PASS] testFailRegisterSessionKeyViaEntrypoint2ndKey() (gas: 471146) +[PASS] testFailRevokeSessionKeyInvalidUser() (gas: 80694) +[PASS] testIncrementCounterDirect() (gas: 50457) +[PASS] testIncrementCounterViaEntrypoint() (gas: 244984) +[PASS] testIncrementCounterViaEntrypointBatching() (gas: 257811) +[PASS] testIncrementCounterViaSessionKey() (gas: 288914) +[PASS] testIncrementCounterViaSessionKeyExpired() (gas: 255246) +[PASS] testIncrementCounterViaSessionKeyWhitelisting() (gas: 312209) +[PASS] testIncrementCounterViaSessionKeyWhitelistingBatch() (gas: 336560) +[PASS] testMintTokenAccount() (gas: 273724) +[PASS] testReceiveNativeToken() (gas: 22294) +[PASS] testRegisterSessionKeyViaEntrypoint() (gas: 390069) +[PASS] testSimulateValidation() (gas: 133659) +[PASS] testTransferOutNativeToken() (gas: 161211) +[PASS] testUpdateEntryPoint() (gas: 810843) +[PASS] testUpgradeAccount() (gas: 2597014) +[PASS] testisValidSignatureTyped() (gas: 56494) +Test result: ok. 31 passed; 0 failed; 0 skipped; finished in 26.70ms + +Running 58 tests for test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol:RecoverableOpenfortAccountTest +[PASS] test3GuardiansCompleteRecovery() (gas: 278772) +[PASS] test3GuardiansFailCompleteRecovery() (gas: 302182) +[PASS] test3GuardiansUnorderedCompleteRecovery() (gas: 304677) +[PASS] test4GuardiansNoDefaultCompleteRecovery() (gas: 470153) +[PASS] testAddEOAGuardian() (gas: 130359) +[PASS] testAddEOAGuardianCancel() (gas: 61181) +[PASS] testAddEOAGuardianDuplicatedPorposal() (gas: 121239) +[PASS] testAddEOAGuardianExpired() (gas: 73211) +[PASS] testAddEOAGuardianExpiredThenReAdd() (gas: 140403) +[PASS] testAddMultipleEOAGuardians() (gas: 446567) +[PASS] testAddOwnerAsGuardianNotAllowed() (gas: 50261) +[PASS] testBasicChecksCompleteRecovery() (gas: 95609) +[PASS] testBasicCompleteRecovery() (gas: 96965) +[PASS] testCancelRecovery() (gas: 92020) +[PASS] testCancelRevokeGuardian() (gas: 143302) +[PASS] testChangeOwnershipAndCountDirect() (gas: 69051) +[PASS] testChangeOwnershipAndCountEntryPoint() (gas: 252176) +[PASS] testCreateAccountViaEntryPoint() (gas: 549754) +[PASS] testCreateAccountWithNonceViaFactory() (gas: 448627) +[PASS] testCreateWrongAccount() (gas: 103051) +[PASS] testCreateWrongFactory() (gas: 48924) +[PASS] testFailIncrementCounterViaSessionKeyNotregistered() (gas: 214613) +[PASS] testFailIncrementCounterViaSessionKeyReachLimit() (gas: 369811) +[PASS] testFailIncrementCounterViaSessionKeyReachLimitBatching() (gas: 266582) +[PASS] testFailIncrementCounterViaSessionKeyRevoked() (gas: 78218) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() (gas: 289145) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingTooBig() (gas: 31650) +[PASS] testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() (gas: 280191) +[PASS] testFailRegisterSessionKeyViaEntrypoint2ndKey() (gas: 471416) +[PASS] testFailRevokeSessionKeyInvalidUser() (gas: 80762) +[PASS] testIncrementCounterDirect() (gas: 50479) +[PASS] testIncrementCounterViaEntrypoint() (gas: 245148) +[PASS] testIncrementCounterViaEntrypointBatching() (gas: 258005) +[PASS] testIncrementCounterViaSessionKey() (gas: 288923) +[PASS] testIncrementCounterViaSessionKeyExpired() (gas: 255388) +[PASS] testIncrementCounterViaSessionKeyWhitelisting() (gas: 312351) +[PASS] testIncrementCounterViaSessionKeyWhitelistingBatch() (gas: 336708) +[PASS] testLockAccount() (gas: 57950) +[PASS] testMessingUpWithGuardianRegister() (gas: 173489) +[PASS] testMintTokenAccount() (gas: 273888) +[PASS] testReceiveNativeToken() (gas: 22317) +[PASS] testRegisterSessionKeyViaEntrypoint() (gas: 390242) +[PASS] testRevokeAllGuardians() (gas: 184050) +[PASS] testRevokeDefaultGuardian() (gas: 149392) +[PASS] testRevokeDefaultGuardianAndAddBack() (gas: 102843) +[PASS] testRevokeEOAGuardianDuplicatedPorposal() (gas: 132586) +[PASS] testRevokeEOAGuardianExpired() (gas: 142699) +[PASS] testRevokeGuardian() (gas: 138904) +[PASS] testSimulateValidation() (gas: 133756) +[PASS] testStartRecovery() (gas: 97714) +[PASS] testStartRecoveryTwice() (gas: 87247) +[PASS] testStubFakeMockTempisGuardian() (gas: 12844) +[PASS] testToken() (gas: 2470) +[PASS] testTransferOutNativeToken() (gas: 161198) +[PASS] testTransferOwner() (gas: 50660) +[PASS] testTransferOwnerNotGuardian() (gas: 110512) +[PASS] testUnlockAccount() (gas: 48731) +[PASS] testUpdateEntryPoint() (gas: 1124342) +Test result: ok. 58 passed; 0 failed; 0 skipped; finished in 26.36ms + +Running 27 tests for test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol:OpenfortPaymasterV2Test +[PASS] testAcceptOwnershipBasic() (gas: 44086) +[PASS] testAcceptOwnershipComplex() (gas: 111339) +[PASS] testDepositsToPaymaster() (gas: 231360) +[PASS] testFailDeposit() (gas: 19320) +[PASS] testFailPaymasterUserOpERC20ValidSigExecBatchInsufficientERC20() (gas: 334989) +[PASS] testFailPaymasterUserOpERC20ValidSigSmallApprove() (gas: 299377) +[PASS] testInitialParameters() (gas: 13783) +[PASS] testParsePaymasterDataERC20() (gas: 43014) +[PASS] testParsePaymasterDataNative() (gas: 37001) +[PASS] testPaymasterStake() (gas: 72146) +[PASS] testPaymasterUserOpERC20FixedExpensiveValidSigExecBatch() (gas: 358331) +[PASS] testPaymasterUserOpERC20FixedValidSig() (gas: 319785) +[PASS] testPaymasterUserOpERC20FixedValidSigDepositor() (gas: 399810) +[PASS] testPaymasterUserOpERC20FixedValidSigExecBatch() (gas: 358354) +[PASS] testPaymasterUserOpERC20ValidSig() (gas: 319823) +[PASS] testPaymasterUserOpERC20ValidSigDepositor() (gas: 399870) +[PASS] testPaymasterUserOpERC20ValidSigDiffMaxPriorityFeePerGas() (gas: 320110) +[PASS] testPaymasterUserOpERC20ValidSigExecBatch() (gas: 358389) +[PASS] testPaymasterUserOpNativeValidSig() (gas: 252384) +[PASS] testPaymasterUserOpNativeValidSigDEPOSITOR() (gas: 345378) +[PASS] testPaymasterUserOpNativeValidSigExecBatch() (gas: 292097) +[PASS] testPaymasterUserOpNativeWrongUserSig() (gas: 182681) +[PASS] testPaymasterUserOpWrongSig() (gas: 123295) +[PASS] testPaymasterUserOpWrongSigLength() (gas: 119272) +[PASS] testSetPostOpGas() (gas: 28167) +[PASS] testToken() (gas: 2471) +[PASS] test_requireFromEntryPoint() (gas: 42885) +Test result: ok. 27 passed; 0 failed; 0 skipped; finished in 27.62ms +| ERC6551Registry contract | | | | | | +|--------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 0 | 0 | | | | | +| Function Name | min | avg | median | max | # calls | +| createAccount | 72709 | 72709 | 72709 | 72709 | 2 | + + +| EntryPoint contract | | | | | | +|-----------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 0 | 0 | | | | | +| Function Name | min | avg | median | max | # calls | +| _validateSenderAndPaymaster | 1149 | 1149 | 1149 | 1149 | 1 | +| depositTo | 24580 | 24580 | 24580 | 24580 | 1 | +| getNonce | 2810 | 2810 | 2810 | 2810 | 1 | +| getUserOpHash | 2181 | 2181 | 2181 | 2181 | 3 | +| handleOps | 96783 | 96783 | 96783 | 96783 | 1 | +| innerHandleOp | 29434 | 29434 | 29434 | 29434 | 1 | +| simulateValidation | 51486 | 51486 | 51486 | 51486 | 1 | + + +| contracts/core/eip6551/EIP6551OpenfortAccount.sol:EIP6551OpenfortAccount contract | | | | | | +|-----------------------------------------------------------------------------------|-----------------|-------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 2394975 | 12125 | | | | | +| Function Name | min | avg | median | max | # calls | +| addDeposit | 10047 | 10047 | 10047 | 10047 | 1 | +| entryPoint | 420 | 1753 | 2420 | 2420 | 3 | +| getDeposit | 1457 | 3623 | 1457 | 7957 | 3 | +| initialize | 2897 | 96529 | 119938 | 119938 | 15 | +| onERC721Received | 839 | 839 | 839 | 839 | 11 | +| owner | 1982 | 2882 | 1982 | 6482 | 5 | + + +| contracts/core/managed/ManagedOpenfortAccount.sol:ManagedOpenfortAccount contract | | | | | | +|-----------------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 2226983 | 11286 | | | | | +| Function Name | min | avg | median | max | # calls | +| acceptOwnership | 4254 | 4254 | 4254 | 4254 | 2 | +| entryPoint | 302 | 302 | 302 | 302 | 1 | +| execute | 3634 | 19918 | 21608 | 32834 | 13 | +| executeBatch | 25580 | 25580 | 25580 | 25580 | 2 | +| initialize | 100252 | 100252 | 100252 | 100252 | 30 | +| owner | 436 | 1436 | 1436 | 2436 | 2 | +| receive | 55 | 55 | 55 | 55 | 2 | +| registerSessionKey | 3376 | 54010 | 49557 | 72146 | 14 | +| revokeSessionKey | 662 | 662 | 662 | 662 | 2 | +| sessionKeys | 1054 | 1054 | 1054 | 1054 | 2 | +| transferOwnership | 739 | 17259 | 24519 | 26519 | 3 | +| validateUserOp | 9487 | 12781 | 11749 | 21144 | 45 | + + +| contracts/core/managed/ManagedOpenfortFactory.sol:ManagedOpenfortFactory contract | | | | | | +|-----------------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 868245 | 4625 | | | | | +| Function Name | min | avg | median | max | # calls | +| accountImplementation | 402 | 402 | 402 | 402 | 1 | +| addStake | 2519 | 23742 | 9778 | 58930 | 3 | +| createAccountWithNonce | 7453 | 248199 | 257219 | 261719 | 32 | +| getAddressWithNonce | 7027 | 7027 | 7027 | 7027 | 4 | +| implementation | 319 | 770 | 319 | 2319 | 155 | +| upgradeTo | 2634 | 4783 | 4783 | 6933 | 2 | + + +| contracts/core/managed/OpenfortBeaconProxy.sol:OpenfortBeaconProxy contract | | | | | | +|-----------------------------------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 162424 | 2010 | | | | | +| Function Name | min | avg | median | max | # calls | +| acceptOwnership | 5187 | 5187 | 5187 | 5187 | 2 | +| entryPoint | 1471 | 1471 | 1471 | 1471 | 3 | +| execute | 4812 | 21794 | 22792 | 34012 | 13 | +| executeBatch | 26860 | 26860 | 26860 | 26860 | 2 | +| implementation | 1079 | 2079 | 2079 | 3079 | 2 | +| owner | 1605 | 7105 | 7105 | 12605 | 2 | +| receive | 10188 | 10188 | 10188 | 10188 | 2 | +| registerSessionKey | 13645 | 62922 | 59747 | 82342 | 14 | +| revokeSessionKey | 1835 | 1835 | 1835 | 1835 | 2 | +| sessionKeys | 2244 | 2244 | 2244 | 2244 | 2 | +| transferOwnership | 1921 | 21432 | 25688 | 36688 | 3 | +| validateUserOp | 10807 | 17024 | 16281 | 24304 | 45 | + + +| contracts/core/recoverable/OpenfortRecoverableProxy.sol:OpenfortRecoverableProxy contract | | | | | | +|-------------------------------------------------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 364257 | 1694 | | | | | +| Function Name | min | avg | median | max | # calls | +| acceptOwnership | 4565 | 4565 | 4565 | 4565 | 3 | +| cancelGuardianProposal | 1148 | 1660 | 1660 | 2173 | 2 | +| cancelGuardianRevocation | 1258 | 1759 | 1759 | 2261 | 2 | +| cancelRecovery | 3060 | 3844 | 3844 | 4628 | 2 | +| completeRecovery | 1294 | 10755 | 10955 | 20133 | 9 | +| confirmGuardianProposal | 1185 | 43455 | 54452 | 59892 | 35 | +| confirmGuardianRevocation | 1080 | 4547 | 1535 | 15312 | 17 | +| eip712Domain | 3588 | 8388 | 11588 | 11588 | 10 | +| entryPoint | 943 | 943 | 943 | 943 | 2 | +| execute | 4158 | 21015 | 22138 | 33358 | 12 | +| executeBatch | 26383 | 26383 | 26383 | 26383 | 2 | +| getGuardians | 10469 | 10469 | 10469 | 10469 | 4 | +| getLock | 895 | 930 | 895 | 1000 | 6 | +| guardianCount | 882 | 2326 | 882 | 7382 | 45 | +| isGuardian | 1139 | 1326 | 1139 | 3139 | 32 | +| isGuardianOrGuardianSigner | 5394 | 5394 | 5394 | 5394 | 1 | +| isLocked | 838 | 1403 | 838 | 7338 | 23 | +| lock | 1158 | 11998 | 3033 | 26383 | 5 | +| owner | 893 | 1143 | 893 | 2893 | 8 | +| proposeGuardian | 1755 | 25694 | 27686 | 38186 | 39 | +| receive | 4976 | 4976 | 4976 | 4976 | 2 | +| registerSessionKey | 10491 | 58659 | 56593 | 79188 | 13 | +| revokeGuardian | 1235 | 14308 | 23331 | 31331 | 19 | +| revokeSessionKey | 1352 | 1352 | 1352 | 1352 | 2 | +| sessionKeys | 1472 | 1472 | 1472 | 1472 | 1 | +| startRecovery | 1145 | 43039 | 55354 | 63954 | 13 | +| transferOwnership | 1295 | 24217 | 35892 | 35892 | 5 | +| unlock | 1112 | 1970 | 1808 | 2990 | 3 | +| updateEntryPoint | 1199 | 1773 | 1236 | 2884 | 3 | +| validateUserOp | 10518 | 15652 | 15345 | 21472 | 41 | + + +| contracts/core/recoverable/RecoverableOpenfortAccount.sol:RecoverableOpenfortAccount contract | | | | | | +|-----------------------------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 3780368 | 19087 | | | | | +| Function Name | min | avg | median | max | # calls | +| acceptOwnership | 4202 | 4202 | 4202 | 4202 | 3 | +| cancelGuardianProposal | 675 | 1241 | 1241 | 1808 | 2 | +| cancelGuardianRevocation | 785 | 1340 | 1340 | 1896 | 2 | +| cancelRecovery | 2590 | 3427 | 3427 | 4264 | 2 | +| completeRecovery | 800 | 10285 | 10431 | 19717 | 9 | +| confirmGuardianProposal | 724 | 43068 | 54086 | 59526 | 35 | +| confirmGuardianRevocation | 619 | 4125 | 1074 | 14946 | 17 | +| eip712Domain | 3071 | 7871 | 11071 | 11071 | 10 | +| entryPoint | 486 | 486 | 486 | 486 | 2 | +| execute | 3686 | 20162 | 21660 | 32886 | 12 | +| executeBatch | 25809 | 25809 | 25809 | 25809 | 2 | +| getGuardians | 5506 | 5506 | 5506 | 5506 | 4 | +| getLock | 438 | 473 | 438 | 543 | 6 | +| guardianCount | 425 | 869 | 425 | 2425 | 45 | +| initialize | 23478 | 280685 | 284902 | 284902 | 62 | +| isGuardian | 679 | 866 | 679 | 2679 | 32 | +| isGuardianOrGuardianSigner | 434 | 434 | 434 | 434 | 1 | +| isLocked | 381 | 554 | 381 | 2381 | 23 | +| lock | 700 | 11541 | 2575 | 25929 | 5 | +| owner | 436 | 686 | 436 | 2436 | 8 | +| proposeGuardian | 1294 | 24543 | 27229 | 33229 | 39 | +| receive | 55 | 55 | 55 | 55 | 2 | +| registerSessionKey | 5428 | 54360 | 51609 | 74198 | 13 | +| revokeGuardian | 762 | 13847 | 22874 | 30874 | 19 | +| revokeSessionKey | 891 | 891 | 891 | 891 | 2 | +| sessionKeys | 988 | 988 | 988 | 988 | 1 | +| startRecovery | 684 | 40158 | 54897 | 58997 | 13 | +| transferOwnership | 834 | 20156 | 30935 | 30935 | 5 | +| unlock | 654 | 1543 | 1445 | 2532 | 3 | +| updateEntryPoint | 738 | 1309 | 763 | 2427 | 3 | +| validateUserOp | 9892 | 13591 | 13861 | 17838 | 41 | + + +| contracts/core/recoverable/RecoverableOpenfortFactory.sol:RecoverableOpenfortFactory contract | | | | | | +|-----------------------------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 660500 | 4104 | | | | | +| Function Name | min | avg | median | max | # calls | +| createAccountWithNonce | 7232 | 400546 | 406912 | 409412 | 62 | +| getAddressWithNonce | 6785 | 6785 | 6785 | 6785 | 4 | + + +| contracts/core/upgradeable/OpenfortUpgradeableProxy.sol:OpenfortUpgradeableProxy contract | | | | | | +|-------------------------------------------------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 200100 | 1534 | | | | | +| Function Name | min | avg | median | max | # calls | +| acceptOwnership | 4583 | 4583 | 4583 | 4583 | 2 | +| eip712Domain | 3499 | 9749 | 9749 | 15999 | 2 | +| entryPoint | 759 | 1364 | 899 | 2899 | 4 | +| execute | 2379 | 22199 | 24355 | 33358 | 21 | +| executeBatch | 26317 | 43493 | 48964 | 55964 | 7 | +| implementation | 335 | 335 | 335 | 335 | 2 | +| isValidSignature | 9743 | 16941 | 20541 | 20541 | 3 | +| owner | 7415 | 7415 | 7415 | 7415 | 1 | +| receive | 4976 | 4976 | 4976 | 4976 | 2 | +| registerSessionKey | 10535 | 58703 | 56637 | 79232 | 13 | +| revokeSessionKey | 1263 | 1263 | 1263 | 1263 | 2 | +| sessionKeys | 1472 | 1472 | 1472 | 1472 | 1 | +| transferOwnership | 7712 | 23554 | 31476 | 31476 | 3 | +| updateEntryPoint | 1192 | 2016 | 2016 | 2840 | 2 | +| upgradeTo | 1431 | 5170 | 5170 | 8909 | 2 | +| validateUserOp | 10452 | 16600 | 18909 | 21597 | 61 | + + +| contracts/core/upgradeable/UpgradeableOpenfortAccount.sol:UpgradeableOpenfortAccount contract | | | | | | +|-----------------------------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 2708230 | 13733 | | | | | +| Function Name | min | avg | median | max | # calls | +| acceptOwnership | 4220 | 4220 | 4220 | 4220 | 2 | +| eip712Domain | 2982 | 6982 | 6982 | 10982 | 2 | +| entryPoint | 442 | 1108 | 442 | 2442 | 3 | +| execute | 1876 | 21503 | 23877 | 32886 | 21 | +| executeBatch | 25743 | 42932 | 48408 | 55408 | 7 | +| initialize | 123977 | 123977 | 123977 | 123977 | 61 | +| isValidSignature | 9256 | 13454 | 15554 | 15554 | 3 | +| owner | 2458 | 2458 | 2458 | 2458 | 1 | +| receive | 55 | 55 | 55 | 55 | 2 | +| registerSessionKey | 5472 | 54404 | 51653 | 74242 | 13 | +| revokeSessionKey | 802 | 802 | 802 | 802 | 2 | +| sessionKeys | 988 | 988 | 988 | 988 | 1 | +| transferOwnership | 2739 | 18592 | 26519 | 26519 | 3 | +| updateEntryPoint | 719 | 1551 | 1551 | 2383 | 2 | +| upgradeTo | 958 | 4705 | 4705 | 8452 | 2 | +| validateUserOp | 9826 | 13662 | 13826 | 17772 | 61 | + + +| contracts/core/upgradeable/UpgradeableOpenfortFactory.sol:UpgradeableOpenfortFactory contract | | | | | | +|-----------------------------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 515288 | 2906 | | | | | +| Function Name | min | avg | median | max | # calls | +| addDeposit | 10210 | 10210 | 10210 | 10210 | 1 | +| createAccountWithNonce | 6266 | 239978 | 243728 | 246228 | 62 | +| entryPoint | 586 | 2836 | 2836 | 5086 | 2 | +| getAddressWithNonce | 5817 | 5817 | 5817 | 5817 | 4 | +| getDeposit | 1623 | 4623 | 1623 | 10623 | 3 | +| initialize | 5589 | 103925 | 120107 | 122607 | 14 | +| onERC721Received | 1035 | 1035 | 1035 | 1035 | 11 | +| owner | 2148 | 4048 | 2148 | 9148 | 5 | + + +| contracts/mock/MockERC20.sol:MockERC20 contract | | | | | | +|-------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 519048 | 2848 | | | | | +| Function Name | min | avg | median | max | # calls | +| mint | 2805 | 23705 | 23705 | 44605 | 4 | +| totalSupply | 326 | 992 | 326 | 2326 | 6 | + + +| contracts/mock/MockERC721.sol:MockERC721 contract | | | | | | +|---------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 1019557 | 5375 | | | | | +| Function Name | min | avg | median | max | # calls | +| mint | 49399 | 49399 | 49399 | 49399 | 11 | +| ownerOf | 536 | 821 | 536 | 2536 | 7 | +| transferFrom | 33314 | 33314 | 33314 | 33314 | 1 | + + +| contracts/mock/MockV2ManagedOpenfortAccount.sol:MockV2ManagedOpenfortAccount contract | | | | | | +|---------------------------------------------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 2087422 | 10589 | | | | | +| Function Name | min | avg | median | max | # calls | +| entryPoint | 302 | 302 | 302 | 302 | 2 | +| initialize | 50379 | 50379 | 50379 | 50379 | 1 | + + +| contracts/mock/MockV2UpgradeableOpenfortAccount.sol:MockV2UpgradeableOpenfortAccount contract | | | | | | +|-----------------------------------------------------------------------------------------------|-----------------|-----|--------|-----|---------| +| Deployment Cost | Deployment Size | | | | | +| 2519811 | 12792 | | | | | +| Function Name | min | avg | median | max | # calls | +| entryPoint | 302 | 302 | 302 | 302 | 1 | +| proxiableUUID | 396 | 396 | 396 | 396 | 1 | + + +| contracts/paymaster/OpenfortPaymasterV2.sol:OpenfortPaymasterV2 contract | | | | | | +|--------------------------------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 1893046 | 9766 | | | | | +| Function Name | min | avg | median | max | # calls | +| acceptOwnership | 5047 | 6277 | 5539 | 8247 | 3 | +| addStake | 520 | 31538 | 32925 | 32925 | 29 | +| deposit | 413 | 30953 | 33846 | 33846 | 30 | +| depositFor | 371 | 39752 | 55506 | 66006 | 9 | +| entryPoint | 261 | 261 | 261 | 261 | 1 | +| getDeposit | 1328 | 2016 | 1328 | 5828 | 45 | +| getDepositFor | 780 | 1633 | 1803 | 5803 | 32 | +| getHash | 4221 | 4270 | 4239 | 4338 | 28 | +| owner | 343 | 1787 | 2343 | 2343 | 18 | +| parsePaymasterAndData | 2111 | 2111 | 2111 | 2111 | 2 | +| postOp | 810 | 19242 | 20570 | 35034 | 16 | +| setPostOpGas | 2516 | 4628 | 2553 | 8815 | 3 | +| transferOwnership | 671 | 16503 | 24419 | 24419 | 3 | +| unlockStake | 3412 | 3412 | 3412 | 3412 | 1 | +| validatePaymasterUserOp | 682 | 10204 | 11233 | 11880 | 17 | +| withdrawDepositorTo | 481 | 3187 | 746 | 10777 | 4 | +| withdrawFromDepositor | 770 | 4236 | 1015 | 10924 | 3 | +| withdrawStake | 1750 | 4128 | 1901 | 8734 | 3 | +| withdrawTo | 628 | 6030 | 3476 | 13987 | 3 | + + +| lib/account-abstraction/contracts/core/SenderCreator.sol:SenderCreator contract | | | | | | +|---------------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 99947 | 531 | | | | | +| Function Name | min | avg | median | max | # calls | +| createSender | 244881 | 304439 | 260372 | 408065 | 3 | + + + + +Ran 6 test suites: 155 tests passed, 0 failed, 0 skipped (155 total tests) diff --git a/gas_reports/2023-11-17_18:19.snap.out b/gas_reports/2023-11-17_18:19.snap.out new file mode 100644 index 0000000..4103873 --- /dev/null +++ b/gas_reports/2023-11-17_18:19.snap.out @@ -0,0 +1,155 @@ +EIP6551OpenfortAccountTest:testCreate2ndAcc() (gas: 208754) +EIP6551OpenfortAccountTest:testCreateAccInitializer() (gas: 22868) +EIP6551OpenfortAccountTest:testCreateAccountWithNonceViaRegistry() (gas: 97697) +EIP6551OpenfortAccountTest:testERC6551Deploy() (gas: 81959) +EIP6551OpenfortAccountTest:testFailCreateAccInitializerNoReinit() (gas: 23223) +EIP6551OpenfortAccountTest:testFailInitializeImplementation() (gas: 10192) +EIP6551OpenfortAccountTest:testFailReinitialize() (gas: 12874) +EIP6551OpenfortAccountTest:testGetDeposit() (gas: 69509) +EIP6551OpenfortAccountTest:testImplementationNoEntryPointAddr() (gas: 7830) +EIP6551OpenfortAccountTest:testNotOwner() (gas: 55688) +EIP6551OpenfortAccountTest:testOwner() (gas: 20505) +ManagedOpenfortAccountTest:testChangeOwnershipAndCountDirect() (gas: 74451) +ManagedOpenfortAccountTest:testChangeOwnershipAndCountEntryPoint() (gas: 251044) +ManagedOpenfortAccountTest:testCreateAccountViaEntryPoint() (gas: 403487) +ManagedOpenfortAccountTest:testCreateAccountWithNonceViaFactory() (gas: 301572) +ManagedOpenfortAccountTest:testFailIncrementCounterViaSessionKeyNotregistered() (gas: 220299) +ManagedOpenfortAccountTest:testFailIncrementCounterViaSessionKeyReachLimit() (gas: 374924) +ManagedOpenfortAccountTest:testFailIncrementCounterViaSessionKeyReachLimitBatching() (gas: 270326) +ManagedOpenfortAccountTest:testFailIncrementCounterViaSessionKeyRevoked() (gas: 81832) +ManagedOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingBatch() (gas: 328842) +ManagedOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() (gas: 292889) +ManagedOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingTooBig() (gas: 34737) +ManagedOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() (gas: 283942) +ManagedOpenfortAccountTest:testFailRegisterSessionKeyViaEntrypoint2ndKey() (gas: 479693) +ManagedOpenfortAccountTest:testFailRevokeSessionKeyInvalidUser() (gas: 84376) +ManagedOpenfortAccountTest:testIncrementCounterDirect() (gas: 53633) +ManagedOpenfortAccountTest:testIncrementCounterViaEntrypoint() (gas: 251465) +ManagedOpenfortAccountTest:testIncrementCounterViaEntrypointBatching() (gas: 264050) +ManagedOpenfortAccountTest:testIncrementCounterViaSessionKey() (gas: 293328) +ManagedOpenfortAccountTest:testIncrementCounterViaSessionKeyExpired() (gas: 259183) +ManagedOpenfortAccountTest:testIncrementCounterViaSessionKeyWhitelisting() (gas: 316800) +ManagedOpenfortAccountTest:testIncrementCounterViaSessionKeyWhitelistingBatch() (gas: 341790) +ManagedOpenfortAccountTest:testMintTokenAccount() (gas: 280183) +ManagedOpenfortAccountTest:testReceiveNativeToken() (gas: 27528) +ManagedOpenfortAccountTest:testRegisterSessionKeyViaEntrypoint() (gas: 397877) +ManagedOpenfortAccountTest:testSimulateValidation() (gas: 389322) +ManagedOpenfortAccountTest:testTransferOutNativeToken() (gas: 165373) +ManagedOpenfortAccountTest:testUpgradeTo() (gas: 2641914) +OpenfortPaymasterV2Test:testAcceptOwnershipBasic() (gas: 44086) +OpenfortPaymasterV2Test:testAcceptOwnershipComplex() (gas: 111339) +OpenfortPaymasterV2Test:testDepositsToPaymaster() (gas: 231360) +OpenfortPaymasterV2Test:testFailDeposit() (gas: 19320) +OpenfortPaymasterV2Test:testFailPaymasterUserOpERC20ValidSigExecBatchInsufficientERC20() (gas: 334989) +OpenfortPaymasterV2Test:testFailPaymasterUserOpERC20ValidSigSmallApprove() (gas: 299377) +OpenfortPaymasterV2Test:testInitialParameters() (gas: 13783) +OpenfortPaymasterV2Test:testParsePaymasterDataERC20() (gas: 43014) +OpenfortPaymasterV2Test:testParsePaymasterDataNative() (gas: 37001) +OpenfortPaymasterV2Test:testPaymasterStake() (gas: 72146) +OpenfortPaymasterV2Test:testPaymasterUserOpERC20FixedExpensiveValidSigExecBatch() (gas: 358331) +OpenfortPaymasterV2Test:testPaymasterUserOpERC20FixedValidSig() (gas: 319785) +OpenfortPaymasterV2Test:testPaymasterUserOpERC20FixedValidSigDepositor() (gas: 399810) +OpenfortPaymasterV2Test:testPaymasterUserOpERC20FixedValidSigExecBatch() (gas: 358354) +OpenfortPaymasterV2Test:testPaymasterUserOpERC20ValidSig() (gas: 319823) +OpenfortPaymasterV2Test:testPaymasterUserOpERC20ValidSigDepositor() (gas: 399870) +OpenfortPaymasterV2Test:testPaymasterUserOpERC20ValidSigDiffMaxPriorityFeePerGas() (gas: 320110) +OpenfortPaymasterV2Test:testPaymasterUserOpERC20ValidSigExecBatch() (gas: 358389) +OpenfortPaymasterV2Test:testPaymasterUserOpNativeValidSig() (gas: 252384) +OpenfortPaymasterV2Test:testPaymasterUserOpNativeValidSigDEPOSITOR() (gas: 345378) +OpenfortPaymasterV2Test:testPaymasterUserOpNativeValidSigExecBatch() (gas: 292097) +OpenfortPaymasterV2Test:testPaymasterUserOpNativeWrongUserSig() (gas: 182681) +OpenfortPaymasterV2Test:testPaymasterUserOpWrongSig() (gas: 123295) +OpenfortPaymasterV2Test:testPaymasterUserOpWrongSigLength() (gas: 119272) +OpenfortPaymasterV2Test:testSetPostOpGas() (gas: 28167) +OpenfortPaymasterV2Test:testToken() (gas: 2471) +OpenfortPaymasterV2Test:test_requireFromEntryPoint() (gas: 42885) +RecoverableOpenfortAccountTest:test3GuardiansCompleteRecovery() (gas: 278772) +RecoverableOpenfortAccountTest:test3GuardiansFailCompleteRecovery() (gas: 302182) +RecoverableOpenfortAccountTest:test3GuardiansUnorderedCompleteRecovery() (gas: 304677) +RecoverableOpenfortAccountTest:test4GuardiansNoDefaultCompleteRecovery() (gas: 470153) +RecoverableOpenfortAccountTest:testAddEOAGuardian() (gas: 130359) +RecoverableOpenfortAccountTest:testAddEOAGuardianCancel() (gas: 61181) +RecoverableOpenfortAccountTest:testAddEOAGuardianDuplicatedPorposal() (gas: 121239) +RecoverableOpenfortAccountTest:testAddEOAGuardianExpired() (gas: 73211) +RecoverableOpenfortAccountTest:testAddEOAGuardianExpiredThenReAdd() (gas: 140403) +RecoverableOpenfortAccountTest:testAddMultipleEOAGuardians() (gas: 446567) +RecoverableOpenfortAccountTest:testAddOwnerAsGuardianNotAllowed() (gas: 50261) +RecoverableOpenfortAccountTest:testBasicChecksCompleteRecovery() (gas: 95609) +RecoverableOpenfortAccountTest:testBasicCompleteRecovery() (gas: 96965) +RecoverableOpenfortAccountTest:testCancelRecovery() (gas: 92020) +RecoverableOpenfortAccountTest:testCancelRevokeGuardian() (gas: 143302) +RecoverableOpenfortAccountTest:testChangeOwnershipAndCountDirect() (gas: 69051) +RecoverableOpenfortAccountTest:testChangeOwnershipAndCountEntryPoint() (gas: 252176) +RecoverableOpenfortAccountTest:testCreateAccountViaEntryPoint() (gas: 549754) +RecoverableOpenfortAccountTest:testCreateAccountWithNonceViaFactory() (gas: 448627) +RecoverableOpenfortAccountTest:testCreateWrongAccount() (gas: 103051) +RecoverableOpenfortAccountTest:testCreateWrongFactory() (gas: 48924) +RecoverableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyNotregistered() (gas: 214613) +RecoverableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyReachLimit() (gas: 369811) +RecoverableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyReachLimitBatching() (gas: 266582) +RecoverableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyRevoked() (gas: 78218) +RecoverableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() (gas: 289145) +RecoverableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingTooBig() (gas: 31650) +RecoverableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() (gas: 280191) +RecoverableOpenfortAccountTest:testFailRegisterSessionKeyViaEntrypoint2ndKey() (gas: 471416) +RecoverableOpenfortAccountTest:testFailRevokeSessionKeyInvalidUser() (gas: 80762) +RecoverableOpenfortAccountTest:testIncrementCounterDirect() (gas: 50479) +RecoverableOpenfortAccountTest:testIncrementCounterViaEntrypoint() (gas: 245148) +RecoverableOpenfortAccountTest:testIncrementCounterViaEntrypointBatching() (gas: 258005) +RecoverableOpenfortAccountTest:testIncrementCounterViaSessionKey() (gas: 288923) +RecoverableOpenfortAccountTest:testIncrementCounterViaSessionKeyExpired() (gas: 255388) +RecoverableOpenfortAccountTest:testIncrementCounterViaSessionKeyWhitelisting() (gas: 312351) +RecoverableOpenfortAccountTest:testIncrementCounterViaSessionKeyWhitelistingBatch() (gas: 336708) +RecoverableOpenfortAccountTest:testLockAccount() (gas: 57950) +RecoverableOpenfortAccountTest:testMessingUpWithGuardianRegister() (gas: 173489) +RecoverableOpenfortAccountTest:testMintTokenAccount() (gas: 273888) +RecoverableOpenfortAccountTest:testReceiveNativeToken() (gas: 22317) +RecoverableOpenfortAccountTest:testRegisterSessionKeyViaEntrypoint() (gas: 390242) +RecoverableOpenfortAccountTest:testRevokeAllGuardians() (gas: 184050) +RecoverableOpenfortAccountTest:testRevokeDefaultGuardian() (gas: 149392) +RecoverableOpenfortAccountTest:testRevokeDefaultGuardianAndAddBack() (gas: 102843) +RecoverableOpenfortAccountTest:testRevokeEOAGuardianDuplicatedPorposal() (gas: 132586) +RecoverableOpenfortAccountTest:testRevokeEOAGuardianExpired() (gas: 142699) +RecoverableOpenfortAccountTest:testRevokeGuardian() (gas: 138904) +RecoverableOpenfortAccountTest:testSimulateValidation() (gas: 133756) +RecoverableOpenfortAccountTest:testStartRecovery() (gas: 97714) +RecoverableOpenfortAccountTest:testStartRecoveryTwice() (gas: 87247) +RecoverableOpenfortAccountTest:testStubFakeMockTempisGuardian() (gas: 12844) +RecoverableOpenfortAccountTest:testToken() (gas: 2470) +RecoverableOpenfortAccountTest:testTransferOutNativeToken() (gas: 161198) +RecoverableOpenfortAccountTest:testTransferOwner() (gas: 50660) +RecoverableOpenfortAccountTest:testTransferOwnerNotGuardian() (gas: 110512) +RecoverableOpenfortAccountTest:testUnlockAccount() (gas: 48731) +RecoverableOpenfortAccountTest:testUpdateEntryPoint() (gas: 1124342) +Specific4337Tests:testToken() (gas: 2404) +UpgradeableOpenfortAccountTest:testChangeOwnershipAndCountDirect() (gas: 73206) +UpgradeableOpenfortAccountTest:testChangeOwnershipAndCountEntryPoint() (gas: 247729) +UpgradeableOpenfortAccountTest:testCreateAccountViaEntryPoint() (gas: 384537) +UpgradeableOpenfortAccountTest:testCreateAccountWithNonceViaFactory() (gas: 282519) +UpgradeableOpenfortAccountTest:testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() (gas: 312280) +UpgradeableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyNotregistered() (gas: 214516) +UpgradeableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyReachLimit() (gas: 369638) +UpgradeableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyReachLimitBatching() (gas: 266411) +UpgradeableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyRevoked() (gas: 78150) +UpgradeableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() (gas: 289083) +UpgradeableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingTooBig() (gas: 31605) +UpgradeableOpenfortAccountTest:testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() (gas: 280116) +UpgradeableOpenfortAccountTest:testFailIsValidSignature() (gas: 53481) +UpgradeableOpenfortAccountTest:testFailIsValidSignatureMessage() (gas: 53571) +UpgradeableOpenfortAccountTest:testFailRegisterSessionKeyViaEntrypoint2ndKey() (gas: 471146) +UpgradeableOpenfortAccountTest:testFailRevokeSessionKeyInvalidUser() (gas: 80694) +UpgradeableOpenfortAccountTest:testIncrementCounterDirect() (gas: 50457) +UpgradeableOpenfortAccountTest:testIncrementCounterViaEntrypoint() (gas: 244984) +UpgradeableOpenfortAccountTest:testIncrementCounterViaEntrypointBatching() (gas: 257811) +UpgradeableOpenfortAccountTest:testIncrementCounterViaSessionKey() (gas: 288914) +UpgradeableOpenfortAccountTest:testIncrementCounterViaSessionKeyExpired() (gas: 255246) +UpgradeableOpenfortAccountTest:testIncrementCounterViaSessionKeyWhitelisting() (gas: 312209) +UpgradeableOpenfortAccountTest:testIncrementCounterViaSessionKeyWhitelistingBatch() (gas: 336560) +UpgradeableOpenfortAccountTest:testMintTokenAccount() (gas: 273724) +UpgradeableOpenfortAccountTest:testReceiveNativeToken() (gas: 22294) +UpgradeableOpenfortAccountTest:testRegisterSessionKeyViaEntrypoint() (gas: 390069) +UpgradeableOpenfortAccountTest:testSimulateValidation() (gas: 133659) +UpgradeableOpenfortAccountTest:testTransferOutNativeToken() (gas: 161211) +UpgradeableOpenfortAccountTest:testUpdateEntryPoint() (gas: 810843) +UpgradeableOpenfortAccountTest:testUpgradeAccount() (gas: 2597014) +UpgradeableOpenfortAccountTest:testisValidSignatureTyped() (gas: 56494) \ No newline at end of file diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 1ea1f4e..aa3e742 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -39,6 +39,7 @@ contract UpgradeableOpenfortAccountTest is Test { keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); event AccountCreated(address indexed account, address indexed accountAdmin); + event AccountImplementationDeployed(address indexed creator); /* * Auxiliary function to generate a userOP @@ -149,6 +150,8 @@ contract UpgradeableOpenfortAccountTest is Test { entryPoint = EntryPoint(payable(targetAddr)); } // deploy upgradeable account implementation + vm.expectEmit(true, true, false, true); + emit AccountImplementationDeployed(factoryAdmin); upgradeableOpenfortAccount = new UpgradeableOpenfortAccount(); // deploy upgradeable account factory upgradeableOpenfortFactory = From 129f161b29cd22e9e74a97fa0457be0094a8511d Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 20 Nov 2023 10:24:17 +0100 Subject: [PATCH 24/83] Updated forge --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index 37a37ab..c22437a 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 37a37ab73364d6644bfe11edf88a07880f99bd56 +Subproject commit c22437a63d1c3418869bc35a7c55a5175a09b701 From a1e24d78173f248b16b5dc75712af3c6f8009bb3 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 20 Nov 2023 14:02:50 +0100 Subject: [PATCH 25/83] Huge refactor under construction --- contracts/core/{recoverable => }/README.md | 0 .../core/{ => base}/BaseOpenfortAccount.sol | 0 contracts/core/base/BaseOpenfortFactory.sol | 46 + .../BaseRecoverableAccount.sol} | 21 +- .../core/eip6551/EIP6551OpenfortAccount.sol | 3 +- .../core/managed/ManagedOpenfortAccount.sol | 20 +- .../core/managed/ManagedOpenfortFactory.sol | 69 +- ...aconProxy.sol => OpenfortManagedProxy.sol} | 4 +- .../recoverable/OpenfortRecoverableProxy.sol | 18 - .../RecoverableOpenfortFactory.sol | 83 - .../upgradeable/OpenfortUpgradeableProxy.sol | 2 +- .../UpgradeableOpenfortAccount.sol | 31 +- .../UpgradeableOpenfortFactory.sol | 82 +- contracts/interfaces/IBaseOpenfortFactory.sol | 20 + .../mock/MockV2ManagedOpenfortAccount.sol | 2 +- .../mock/MockV2UpgradeableOpenfortAccount.sol | 2 +- script/deployRecoverableAccounts.sol | 60 +- .../managed/ManagedOpenfortAccountTest.t.sol | 4 +- .../RecoverableOpenfortAccountTest.t.sol | 4590 ++++++++--------- 19 files changed, 2513 insertions(+), 2544 deletions(-) rename contracts/core/{recoverable => }/README.md (100%) rename contracts/core/{ => base}/BaseOpenfortAccount.sol (100%) create mode 100644 contracts/core/base/BaseOpenfortFactory.sol rename contracts/core/{recoverable/RecoverableOpenfortAccount.sol => base/BaseRecoverableAccount.sol} (95%) rename contracts/core/managed/{OpenfortBeaconProxy.sol => OpenfortManagedProxy.sol} (82%) delete mode 100644 contracts/core/recoverable/OpenfortRecoverableProxy.sol delete mode 100644 contracts/core/recoverable/RecoverableOpenfortFactory.sol diff --git a/contracts/core/recoverable/README.md b/contracts/core/README.md similarity index 100% rename from contracts/core/recoverable/README.md rename to contracts/core/README.md diff --git a/contracts/core/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol similarity index 100% rename from contracts/core/BaseOpenfortAccount.sol rename to contracts/core/base/BaseOpenfortAccount.sol diff --git a/contracts/core/base/BaseOpenfortFactory.sol b/contracts/core/base/BaseOpenfortFactory.sol new file mode 100644 index 0000000..0c849cf --- /dev/null +++ b/contracts/core/base/BaseOpenfortFactory.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; + +/** + * @title BaseOpenfortFactory (Non-upgradeable) + * @notice Contract to create an on-chain factory to deploy new OpenfortAccounts. + * It inherits from: + * - IBaseOpenfortFactory + */ +abstract contract BaseOpenfortFactory is IBaseOpenfortFactory { + address public entrypointContract; + address public accountImplementation; + uint256 public recoveryPeriod; + uint256 public securityPeriod; + uint256 public securityWindow; + uint256 public lockPeriod; + address public openfortGuardian; + + error InsecurePeriod(); + + constructor( + address _entrypoint, + address _accountImplementation, + uint256 _recoveryPeriod, + uint256 _securityPeriod, + uint256 _securityWindow, + uint256 _lockPeriod, + address _openfortGuardian + ) { + if (_entrypoint == address(0) || _accountImplementation == address(0) || _openfortGuardian == address(0)) { + revert ZeroAddressNotAllowed(); + } + if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { + revert InsecurePeriod(); + } + entrypointContract = _entrypoint; + accountImplementation = _accountImplementation; + recoveryPeriod = _recoveryPeriod; + securityPeriod = _securityPeriod; + securityWindow = _securityWindow; + lockPeriod = _lockPeriod; + openfortGuardian = _openfortGuardian; + } +} diff --git a/contracts/core/recoverable/RecoverableOpenfortAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol similarity index 95% rename from contracts/core/recoverable/RecoverableOpenfortAccount.sol rename to contracts/core/base/BaseRecoverableAccount.sol index bf523ba..fa1ef2d 100644 --- a/contracts/core/recoverable/RecoverableOpenfortAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -5,19 +5,17 @@ import { Ownable2StepUpgradeable, OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; - -import {BaseOpenfortAccount, IEntryPoint, SafeCastUpgradeable, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; +import {BaseOpenfortAccount, IEntryPoint, ECDSAUpgradeable} from "../base/BaseOpenfortAccount.sol"; /** * @title RecoverableOpenfortAccount * @notice Openfort account with session keys, guardians and pausability following the ERC-4337 standard. * It inherits from: * - BaseOpenfortAccount - * - UUPSUpgradeable + * - Ownable2StepUpgradeable */ -contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable, UUPSUpgradeable { +abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpgradeable { using ECDSAUpgradeable for bytes32; address internal entrypointContract; @@ -122,8 +120,6 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradea emit GuardianAdded(_openfortGuardian); } - function _authorizeUpgrade(address) internal override onlyOwner {} - /** * Return the current EntryPoint */ @@ -135,15 +131,6 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradea return OwnableUpgradeable.owner(); } - /** - * Update the EntryPoint address - */ - function updateEntryPoint(address _newEntrypoint) external onlyOwner { - if (_newEntrypoint == address(0)) revert ZeroAddressNotAllowed(); - emit EntryPointUpdated(entrypointContract, _newEntrypoint); - entrypointContract = _newEntrypoint; - } - /** * Locking functionalities * */ @@ -238,7 +225,7 @@ contract RecoverableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradea /** * @notice Lets the owner propose a guardian to its Openfort account. - * The first guardian is added immediately (see constructor). All following proposals must be confirmed + * The first guardians are added when the account is created. All following proposals must be confirmed * by calling the confirmGuardianProposal() method. Only the owner can add guardians. * Guardians must either be an EOA or a contract with an owner() (ERC-173). * @param _guardian The guardian to propose. diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index 24f6551..45ea319 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -8,8 +8,7 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC6551Account} from "erc6551/src/interfaces/IERC6551Account.sol"; import {IERC6551Executable} from "erc6551/src/interfaces/IERC6551Executable.sol"; import {ERC6551AccountLib} from "erc6551/src/lib/ERC6551AccountLib.sol"; - -import {BaseOpenfortAccount, IEntryPoint, ECDSAUpgradeable} from "../BaseOpenfortAccount.sol"; +import {BaseOpenfortAccount, IEntryPoint, ECDSAUpgradeable} from "../base/BaseOpenfortAccount.sol"; /** * @title EIP6551OpenfortAccount (Non-upgradeable) diff --git a/contracts/core/managed/ManagedOpenfortAccount.sol b/contracts/core/managed/ManagedOpenfortAccount.sol index 4aa0f01..06e7338 100644 --- a/contracts/core/managed/ManagedOpenfortAccount.sol +++ b/contracts/core/managed/ManagedOpenfortAccount.sol @@ -1,34 +1,22 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -// Base account contract to inherit from and EntryPoint interface +import {BaseRecoverableAccount, IEntryPoint} from "../base/BaseRecoverableAccount.sol"; import { Ownable2StepUpgradeable, OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {BaseOpenfortAccount, IEntryPoint} from "../BaseOpenfortAccount.sol"; /** * @title ManagedOpenfortAccount (Upgradeable via Beacon) * @notice Smart contract wallet managed via Beacon with session keys following the ERC-4337 standard. * It inherits from: - * - BaseOpenfortAccount + * - BaseRecoverableAccount */ -contract ManagedOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable { +contract ManagedOpenfortAccount is BaseRecoverableAccount { address private constant ENTRYPOINTCONTRACT = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; - /* - * @notice Initialize the smart contract wallet. - */ - function initialize(address _defaultAdmin) public initializer { - if (_defaultAdmin == address(0)) { - revert ZeroAddressNotAllowed(); - } - _transferOwnership(_defaultAdmin); - __EIP712_init("Openfort", "0.5"); - } - - function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { + function owner() public view virtual override returns (address) { return OwnableUpgradeable.owner(); } diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index 402781f..e1ae1e2 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -6,31 +6,46 @@ import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/Upgradeabl // Smart wallet implementation to use import {ManagedOpenfortAccount} from "./ManagedOpenfortAccount.sol"; -import {OpenfortBeaconProxy} from "./OpenfortBeaconProxy.sol"; +import {OpenfortManagedProxy} from "./OpenfortManagedProxy.sol"; // Interfaces -import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; +import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; /** * @title ManagedOpenfortFactory (Non-upgradeable) * @notice Contract to create an on-chain factory to deploy new ManagedOpenfortAccounts. - * It uses OpenZeppelin's Create2 and OpenfortBeaconProxy libraries. + * It uses OpenZeppelin's Create2 and OpenfortManagedProxy libraries. * It inherits from: * - IBaseOpenfortFactory * - UpgradeableBeacon to also work as the beacon */ -contract ManagedOpenfortFactory is IBaseOpenfortFactory, UpgradeableBeacon { - address internal entrypointContract; - - constructor(address _owner, address _entrypoint, address _accountImplementation) +contract ManagedOpenfortFactory is BaseOpenfortFactory, UpgradeableBeacon { + constructor( + address _owner, + address _entrypoint, + address _accountImplementation, + uint256 _recoveryPeriod, + uint256 _securityPeriod, + uint256 _securityWindow, + uint256 _lockPeriod, + address _openfortGuardian + ) + BaseOpenfortFactory( + _entrypoint, + _accountImplementation, + _recoveryPeriod, + _securityPeriod, + _securityWindow, + _lockPeriod, + _openfortGuardian + ) UpgradeableBeacon(_accountImplementation) { - if (_owner == address(0) || _entrypoint == address(0) || _accountImplementation == address(0)) { + if (_owner == address(0)) { revert ZeroAddressNotAllowed(); } _transferOwnership(_owner); - entrypointContract = _entrypoint; } /* @@ -43,11 +58,10 @@ contract ManagedOpenfortFactory is IBaseOpenfortFactory, UpgradeableBeacon { if (account.code.length != 0) return account; emit AccountCreated(account, _admin); - account = address( - new OpenfortBeaconProxy{salt: salt}( - address(this), - abi.encodeCall(ManagedOpenfortAccount.initialize, (_admin)) - ) + + account = address(new OpenfortManagedProxy{salt: salt}(address(this), "")); + ManagedOpenfortAccount(payable(account)).initialize( + _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, openfortGuardian ); } @@ -60,23 +74,32 @@ contract ManagedOpenfortFactory is IBaseOpenfortFactory, UpgradeableBeacon { salt, keccak256( abi.encodePacked( - type(OpenfortBeaconProxy).creationCode, - abi.encode(address(this), abi.encodeCall(ManagedOpenfortAccount.initialize, (_admin))) + type(OpenfortManagedProxy).creationCode, + abi.encode(address(this), "") ) ) ); } - function accountImplementation() external view override returns (address) { - return implementation(); - } - + /** - * Add stake for this factory. - * This method can also carry eth value to add to the current stake. - * @param unstakeDelaySec - the unstake delay for this factory. Can only be increased. + * @dev {See BaseOpenfortFactory} */ function addStake(uint32 unstakeDelaySec) external payable onlyOwner { IEntryPoint(entrypointContract).addStake{value: msg.value}(unstakeDelaySec); } + + /** + * @dev {See BaseOpenfortFactory} + */ + function unlockStake() external onlyOwner { + IEntryPoint(entrypointContract).unlockStake(); + } + + /** + * @dev {See BaseOpenfortFactory} + */ + function withdrawStake(address payable withdrawAddress) external onlyOwner { + IEntryPoint(entrypointContract).withdrawStake(withdrawAddress); + } } diff --git a/contracts/core/managed/OpenfortBeaconProxy.sol b/contracts/core/managed/OpenfortManagedProxy.sol similarity index 82% rename from contracts/core/managed/OpenfortBeaconProxy.sol rename to contracts/core/managed/OpenfortManagedProxy.sol index 57565ac..7d835e5 100644 --- a/contracts/core/managed/OpenfortBeaconProxy.sol +++ b/contracts/core/managed/OpenfortManagedProxy.sol @@ -4,12 +4,12 @@ pragma solidity =0.8.19; import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; /** - * @title OpenfortBeaconProxy (Non-upgradeable) + * @title OpenfortManagedProxy (Non-upgradeable) * @notice Contract to create the beacon. It determines the implementation contract. * It inherits from: * - BeaconProxy */ -contract OpenfortBeaconProxy is BeaconProxy { +contract OpenfortManagedProxy is BeaconProxy { constructor(address beacon, bytes memory data) BeaconProxy(beacon, data) {} function implementation() external view returns (address) { diff --git a/contracts/core/recoverable/OpenfortRecoverableProxy.sol b/contracts/core/recoverable/OpenfortRecoverableProxy.sol deleted file mode 100644 index 1982d83..0000000 --- a/contracts/core/recoverable/OpenfortRecoverableProxy.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - -/** - * @title OpenfortRecoverableProxy - * @notice Contract to create the proxies - * It inherits from: - * - ERC1967Proxy - */ -contract OpenfortRecoverableProxy is ERC1967Proxy { - constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) {} - - function implementation() external view returns (address) { - return _getImplementation(); - } -} diff --git a/contracts/core/recoverable/RecoverableOpenfortFactory.sol b/contracts/core/recoverable/RecoverableOpenfortFactory.sol deleted file mode 100644 index fa14f0b..0000000 --- a/contracts/core/recoverable/RecoverableOpenfortFactory.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; -// Smart wallet implementation to use -import {RecoverableOpenfortAccount} from "./RecoverableOpenfortAccount.sol"; -import {OpenfortRecoverableProxy} from "./OpenfortRecoverableProxy.sol"; -// Interfaces -import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; - -/** - * @title RecoverableOpenfortFactory (Non-upgradeable) - * @notice Contract to create an on-chain factory to deploy new RecoverableOpenfortAccounts. - * It uses OpenZeppelin's Create2 and OpenfortRecoverableProxy libraries. - * It inherits from: - * - IBaseOpenfortFactory - */ -contract RecoverableOpenfortFactory is IBaseOpenfortFactory { - address public immutable entrypointContract; - address public immutable accountImplementation; - uint256 public immutable recoveryPeriod; - uint256 public immutable securityPeriod; - uint256 public immutable securityWindow; - uint256 public immutable lockPeriod; - address public immutable openfortGuardian; - - error InsecurePeriod(); - - constructor( - address _entrypoint, - address _accountImplementation, - uint256 _recoveryPeriod, - uint256 _securityPeriod, - uint256 _securityWindow, - uint256 _lockPeriod, - address _openfortGuardian - ) { - if (_entrypoint == address(0) || _accountImplementation == address(0) || _openfortGuardian == address(0)) { - revert ZeroAddressNotAllowed(); - } - if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { - revert InsecurePeriod(); - } - entrypointContract = _entrypoint; - accountImplementation = _accountImplementation; - recoveryPeriod = _recoveryPeriod; - securityPeriod = _securityPeriod; - securityWindow = _securityWindow; - lockPeriod = _lockPeriod; - openfortGuardian = _openfortGuardian; - } - - /* - * @notice Deploy a new account for _admin with a nonce. - */ - function createAccountWithNonce(address _admin, bytes32 _nonce) external returns (address account) { - bytes32 salt = keccak256(abi.encode(_admin, _nonce)); - account = getAddressWithNonce(_admin, _nonce); - - if (account.code.length > 0) { - return account; - } - - emit AccountCreated(account, _admin); - account = address(new OpenfortRecoverableProxy{salt: salt}(accountImplementation, "")); - RecoverableOpenfortAccount(payable(account)).initialize( - _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, openfortGuardian - ); - } - - /* - * @notice Return the address of an account that would be deployed with the given admin signer and nonce. - */ - function getAddressWithNonce(address _admin, bytes32 _nonce) public view returns (address) { - bytes32 salt = keccak256(abi.encode(_admin, _nonce)); - return Create2.computeAddress( - salt, - keccak256( - abi.encodePacked(type(OpenfortRecoverableProxy).creationCode, abi.encode(accountImplementation, "")) - ) - ); - } -} diff --git a/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol b/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol index 63fc87a..06628ca 100644 --- a/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol +++ b/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol @@ -13,6 +13,6 @@ contract OpenfortUpgradeableProxy is ERC1967Proxy { constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) {} function implementation() external view returns (address) { - return _getImplementation(); + return _implementation(); } } diff --git a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol index b6ad9d9..562bc7a 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol @@ -2,7 +2,7 @@ pragma solidity =0.8.19; // Base account contract to inherit from and EntryPoint interface -import {BaseOpenfortAccount, IEntryPoint} from "../BaseOpenfortAccount.sol"; +import {BaseRecoverableAccount, IEntryPoint} from "../base/BaseRecoverableAccount.sol"; import { Ownable2StepUpgradeable, OwnableUpgradeable @@ -14,37 +14,14 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. * It inherits from: * - BaseOpenfortAccount - * - Ownable2StepUpgradeable * - UUPSUpgradeable */ -contract UpgradeableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable, UUPSUpgradeable { - address internal entrypointContract; - +contract UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); - /* - * @notice Initialize the smart contract wallet. - */ - function initialize(address _defaultAdmin, address _entrypoint) public initializer { - if (_defaultAdmin == address(0) || _entrypoint == address(0)) { - revert ZeroAddressNotAllowed(); - } - emit EntryPointUpdated(entrypointContract, _entrypoint); - _transferOwnership(_defaultAdmin); - entrypointContract = _entrypoint; - __EIP712_init("Openfort", "0.5"); - } - function _authorizeUpgrade(address) internal override onlyOwner {} - /** - * Return the current EntryPoint - */ - function entryPoint() public view override returns (IEntryPoint) { - return IEntryPoint(entrypointContract); - } - - function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { + function owner() public view virtual override(BaseRecoverableAccount, OwnableUpgradeable) returns (address) { return OwnableUpgradeable.owner(); } @@ -58,4 +35,6 @@ contract UpgradeableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradea emit EntryPointUpdated(entrypointContract, _newEntrypoint); entrypointContract = _newEntrypoint; } + + function _authorizeUpgrade(address) internal override onlyOwner {} } diff --git a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol index 7e2411b..dd1f893 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol @@ -2,29 +2,44 @@ pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; -// Smart wallet implementation to use -import {UpgradeableOpenfortAccount} from "./UpgradeableOpenfortAccount.sol"; +import { + Ownable2StepUpgradeable, + OwnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {UpgradeableOpenfortAccount, IEntryPoint} from "./UpgradeableOpenfortAccount.sol"; import {OpenfortUpgradeableProxy} from "./OpenfortUpgradeableProxy.sol"; -// Interfaces -import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; +import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; /** * @title UpgradeableOpenfortFactory (Non-upgradeable) * @notice Contract to create an on-chain factory to deploy new UpgradeableOpenfortAccounts. * It uses OpenZeppelin's Create2 and OpenfortUpgradeableProxy libraries. * It inherits from: - * - IBaseOpenfortFactory + * - BaseOpenfortFactory + * - Ownable2StepUpgradeable */ -contract UpgradeableOpenfortFactory is IBaseOpenfortFactory { - address public immutable entrypointContract; - address public immutable accountImplementation; - - constructor(address _entrypoint, address _accountImplementation) { - if (_entrypoint == address(0) || _accountImplementation == address(0)) { - revert ZeroAddressNotAllowed(); - } - entrypointContract = _entrypoint; - accountImplementation = _accountImplementation; +contract UpgradeableOpenfortFactory is BaseOpenfortFactory, Ownable2StepUpgradeable { + constructor( + address _owner, + address _entrypoint, + address _accountImplementation, + uint256 _recoveryPeriod, + uint256 _securityPeriod, + uint256 _securityWindow, + uint256 _lockPeriod, + address _openfortGuardian + ) + BaseOpenfortFactory( + _entrypoint, + _accountImplementation, + _recoveryPeriod, + _securityPeriod, + _securityWindow, + _lockPeriod, + _openfortGuardian + ) + { + _transferOwnership(_owner); } /* @@ -39,11 +54,9 @@ contract UpgradeableOpenfortFactory is IBaseOpenfortFactory { } emit AccountCreated(account, _admin); - account = address( - new OpenfortUpgradeableProxy{salt: salt}( - accountImplementation, - abi.encodeCall(UpgradeableOpenfortAccount.initialize, (_admin, entrypointContract)) - ) + account = address(new OpenfortUpgradeableProxy{salt: salt}(accountImplementation, "")); + UpgradeableOpenfortAccount(payable(account)).initialize( + _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, openfortGuardian ); } @@ -55,14 +68,29 @@ contract UpgradeableOpenfortFactory is IBaseOpenfortFactory { return Create2.computeAddress( salt, keccak256( - abi.encodePacked( - type(OpenfortUpgradeableProxy).creationCode, - abi.encode( - accountImplementation, - abi.encodeCall(UpgradeableOpenfortAccount.initialize, (_admin, entrypointContract)) - ) - ) + abi.encodePacked(type(OpenfortUpgradeableProxy).creationCode, abi.encode(accountImplementation, "")) ) ); } + + /** + * @dev {See BaseOpenfortFactory} + */ + function addStake(uint32 unstakeDelaySec) external payable onlyOwner { + IEntryPoint(entrypointContract).addStake{value: msg.value}(unstakeDelaySec); + } + + /** + * @dev {See BaseOpenfortFactory} + */ + function unlockStake() external onlyOwner { + IEntryPoint(entrypointContract).unlockStake(); + } + + /** + * @dev {See BaseOpenfortFactory} + */ + function withdrawStake(address payable withdrawAddress) external onlyOwner { + IEntryPoint(entrypointContract).withdrawStake(withdrawAddress); + } } diff --git a/contracts/interfaces/IBaseOpenfortFactory.sol b/contracts/interfaces/IBaseOpenfortFactory.sol index 43949f3..fb8278c 100644 --- a/contracts/interfaces/IBaseOpenfortFactory.sol +++ b/contracts/interfaces/IBaseOpenfortFactory.sol @@ -16,4 +16,24 @@ interface IBaseOpenfortFactory { /// @notice Returns the address of an Account that would be deployed with the given admin and nonce. function getAddressWithNonce(address _admin, bytes32 _nonce) external view returns (address); + + /** + * Add to the factory's stake - amount and delay + * any pending unstake is first cancelled. + * @param _unstakeDelaySec the new lock duration before the deposit can be withdrawn. + */ + function addStake(uint32 _unstakeDelaySec) external payable; + + /** + * Attempt to unlock the stake. + * the value can be withdrawn (using withdrawStake) after the unstake delay. + */ + function unlockStake() external; + + /** + * Withdraw from the (unlocked) stake. + * must first call unlockStake and wait for the unstakeDelay to pass + * @param withdrawAddress the address to send withdrawn value. + */ + function withdrawStake(address payable withdrawAddress) external; } diff --git a/contracts/mock/MockV2ManagedOpenfortAccount.sol b/contracts/mock/MockV2ManagedOpenfortAccount.sol index eca5520..e606c1f 100644 --- a/contracts/mock/MockV2ManagedOpenfortAccount.sol +++ b/contracts/mock/MockV2ManagedOpenfortAccount.sol @@ -7,7 +7,7 @@ import { } from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; // Base account contract to inherit from -import {BaseOpenfortAccount, IEntryPoint} from "../core/BaseOpenfortAccount.sol"; +import {BaseOpenfortAccount, IEntryPoint} from "../core/base/BaseOpenfortAccount.sol"; /** * @title ManagedOpenfortAccount (Upgradeable via Beacon) diff --git a/contracts/mock/MockV2UpgradeableOpenfortAccount.sol b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol index f3a1c71..81588ab 100644 --- a/contracts/mock/MockV2UpgradeableOpenfortAccount.sol +++ b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol @@ -8,7 +8,7 @@ import { import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; // Base account contract to inherit from -import {BaseOpenfortAccount, IEntryPoint} from "../core/BaseOpenfortAccount.sol"; +import {BaseOpenfortAccount, IEntryPoint} from "../core/base/BaseOpenfortAccount.sol"; /** * @title MockV2UpgradeableOpenfortAccount diff --git a/script/deployRecoverableAccounts.sol b/script/deployRecoverableAccounts.sol index eef51e8..e841a12 100644 --- a/script/deployRecoverableAccounts.sol +++ b/script/deployRecoverableAccounts.sol @@ -1,39 +1,39 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Script, console} from "forge-std/Script.sol"; -import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; -import {RecoverableOpenfortAccount} from "../contracts/core/recoverable/RecoverableOpenfortAccount.sol"; -import {RecoverableOpenfortFactory} from "../contracts/core/recoverable/RecoverableOpenfortFactory.sol"; +// import {Script, console} from "forge-std/Script.sol"; +// import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; +// import {RecoverableOpenfortAccount} from "../contracts/core/recoverable/RecoverableOpenfortAccount.sol"; +// import {RecoverableOpenfortFactory} from "../contracts/core/recoverable/RecoverableOpenfortFactory.sol"; -contract RecoverableOpenfortDeploy is Script { - uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); - // uint256 internal deployPrivKey = vm.envUint("PK"); - address internal deployAddress = vm.addr(deployPrivKey); - IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); +// contract RecoverableOpenfortDeploy is Script { +// uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); +// // uint256 internal deployPrivKey = vm.envUint("PK"); +// address internal deployAddress = vm.addr(deployPrivKey); +// IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - uint256 private constant RECOVERY_PERIOD = 2 days; - uint256 private constant SECURITY_PERIOD = 1.5 days; - uint256 private constant SECURITY_WINDOW = 0.5 days; - uint256 private constant LOCK_PERIOD = 5 days; - address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_TESTNET"); +// uint256 private constant RECOVERY_PERIOD = 2 days; +// uint256 private constant SECURITY_PERIOD = 1.5 days; +// uint256 private constant SECURITY_WINDOW = 0.5 days; +// uint256 private constant LOCK_PERIOD = 5 days; +// address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_TESTNET"); - function run() public { - bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); - vm.startBroadcast(deployPrivKey); +// function run() public { +// bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); +// vm.startBroadcast(deployPrivKey); - RecoverableOpenfortAccount recoverableOpenfortAccountImpl = new RecoverableOpenfortAccount{salt: versionSalt}(); +// RecoverableOpenfortAccount recoverableOpenfortAccountImpl = new RecoverableOpenfortAccount{salt: versionSalt}(); - RecoverableOpenfortFactory recoverableOpenfortFactory = - new RecoverableOpenfortFactory{salt: versionSalt}(address(entryPoint), address(recoverableOpenfortAccountImpl), RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, OPENFORT_GUARDIAN); - // (upgradeableOpenfortFactory); - // address account1 = upgradeableOpenfortFactory.accountImplementation(); - // The first call should create a new account, while the second will just return the corresponding account address - address account2 = recoverableOpenfortFactory.createAccountWithNonce(deployAddress, "1"); - console.log( - "Factory at address %s has created an account at address %s", address(recoverableOpenfortFactory), account2 - ); +// RecoverableOpenfortFactory recoverableOpenfortFactory = +// new RecoverableOpenfortFactory{salt: versionSalt}(address(entryPoint), address(recoverableOpenfortAccountImpl), RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, OPENFORT_GUARDIAN); +// // (upgradeableOpenfortFactory); +// // address account1 = upgradeableOpenfortFactory.accountImplementation(); +// // The first call should create a new account, while the second will just return the corresponding account address +// address account2 = recoverableOpenfortFactory.createAccountWithNonce(deployAddress, "1"); +// console.log( +// "Factory at address %s has created an account at address %s", address(recoverableOpenfortFactory), account2 +// ); - vm.stopBroadcast(); - } -} +// vm.stopBroadcast(); +// } +// } diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index d0487a4..e237259 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -8,7 +8,7 @@ import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC20} from "contracts/mock/MockERC20.sol"; import {ManagedOpenfortAccount} from "contracts/core/managed/ManagedOpenfortAccount.sol"; import {ManagedOpenfortFactory} from "contracts/core/managed/ManagedOpenfortFactory.sol"; -import {OpenfortBeaconProxy} from "contracts/core/managed/OpenfortBeaconProxy.sol"; +import {OpenfortManagedProxy} from "contracts/core/managed/OpenfortManagedProxy.sol"; import {MockV2ManagedOpenfortAccount} from "contracts/mock/MockV2ManagedOpenfortAccount.sol"; contract ManagedOpenfortAccountTest is Test { @@ -1023,7 +1023,7 @@ contract ManagedOpenfortAccountTest is Test { assertEq(managedAccount.owner(), accountAdmin); assertEq(address(managedAccount.entryPoint()), address(entryPoint)); - OpenfortBeaconProxy p = OpenfortBeaconProxy(payable(account)); + OpenfortManagedProxy p = OpenfortManagedProxy(payable(account)); // Printing account address and the implementation address console.log(account); console.log(p.implementation()); diff --git a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol index 24c8616..f6afa3f 100644 --- a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol +++ b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol @@ -1,2363 +1,2363 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Test, console} from "lib/forge-std/src/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; -import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {TestToken} from "account-abstraction/test/TestToken.sol"; -import {RecoverableOpenfortAccount} from "contracts/core/recoverable/RecoverableOpenfortAccount.sol"; -import {RecoverableOpenfortFactory} from "contracts/core/recoverable/RecoverableOpenfortFactory.sol"; -import {OpenfortRecoverableProxy} from "contracts/core/recoverable/OpenfortRecoverableProxy.sol"; - -contract RecoverableOpenfortAccountTest is Test { - using ECDSA for bytes32; - - EntryPoint public entryPoint; - RecoverableOpenfortAccount public recoverableOpenfortAccountImpl; - RecoverableOpenfortFactory public recoverableOpenfortFactory; - address public account; - TestCounter public testCounter; - TestToken public testToken; - - // Testing addresses - address private factoryAdmin; - uint256 private factoryAdminPKey; - - address private accountAdmin; - uint256 private accountAdminPKey; - - address payable private beneficiary = payable(makeAddr("beneficiary")); - - uint256 private constant RECOVERY_PERIOD = 36; - uint256 private constant SECURITY_PERIOD = 24; - uint256 private constant SECURITY_WINDOW = 12; - uint256 private constant LOCK_PERIOD = 50; - address private OPENFORT_GUARDIAN; - uint256 private OPENFORT_GUARDIAN_PKEY; - - event AccountCreated(address indexed account, address indexed accountAdmin); - event GuardianProposed(address indexed guardian, uint256 executeAfter); - event GuardianProposalCancelled(address indexed guardian); - event GuardianRevocationRequested(address indexed guardian, uint256 executeAfter); - event GuardianRevocationCancelled(address indexed guardian); - - error ZeroAddressNotAllowed(); - error AccountLocked(); - error AccountNotLocked(); - error MustBeGuardian(); - error DuplicatedGuardian(); - error UnknownProposal(); - error PendingProposalNotOver(); - error PendingProposalExpired(); - error DuplicatedRevoke(); - error UnknownRevoke(); - error PendingRevokeNotOver(); - error PendingRevokeExpired(); - error GuardianCannotBeOwner(); - error NoOngoingRecovery(); - error OngoingRecovery(); - error InvalidRecoverySignatures(); - error InvalidSignatureAmount(); - - // keccak256("Recover(address recoveryAddress,uint64 executeAfter,uint32 guardiansRequired)"); - bytes32 private RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; - bytes32 private constant _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (UserOperation[] memory ops) { - uint256 nonce = entryPoint.getNonce(sender, 0); - - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 0, - maxPriorityFeePerGas: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /** - * @notice Initialize the RecoverableOpenfortAccount testing contract. - * Scenario: - * - factoryAdmin is the deployer (and owner) of the RecoverableOpenfortFactory - * - accountAdmin is the account used to deploy new upgradeable accounts - * - entryPoint is the singleton EntryPoint - * - testCounter is the counter used to test userOps - */ - function setUp() public { - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - - vm.startPrank(factoryAdmin); - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } - (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); - // deploy upgradeable account implementation - recoverableOpenfortAccountImpl = new RecoverableOpenfortAccount(); - // deploy upgradeable account factory - recoverableOpenfortFactory = new RecoverableOpenfortFactory( - payable(address(entryPoint)), - address(recoverableOpenfortAccountImpl), - RECOVERY_PERIOD, - SECURITY_PERIOD, - SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN - ); - - // Create an upgradeable account wallet and get its address - account = recoverableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); - - // deploy a new TestCounter - testCounter = new TestCounter(); - // deploy a new TestToken (ERC20) - testToken = new TestToken(); - vm.stopPrank(); - } - - function testCreateWrongFactory() public { - vm.expectRevert(ZeroAddressNotAllowed.selector); - vm.prank(factoryAdmin); - recoverableOpenfortFactory = new RecoverableOpenfortFactory( - payable(address(entryPoint)), - address(0), - RECOVERY_PERIOD, - SECURITY_PERIOD, - SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN - ); - } - - function testCreateWrongAccount() public { - vm.expectRevert(ZeroAddressNotAllowed.selector); - vm.prank(accountAdmin); - account = address( - new OpenfortRecoverableProxy{salt: 0}( - address(recoverableOpenfortAccountImpl), - abi.encodeCall( - RecoverableOpenfortAccount.initialize, - ( - address(0x0), - address(entryPoint), - RECOVERY_PERIOD, - SECURITY_PERIOD, - SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN) - ) - ) - ); - } - - /* - * Create an account by directly calling the factory. - */ - function testCreateAccountWithNonceViaFactory() public { - // Get the counterfactual address - vm.prank(factoryAdmin); - address accountAddress2 = recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, "2"); - - // Expect that we will see an event containing the account and admin - vm.expectEmit(true, true, false, true); - emit AccountCreated(accountAddress2, accountAdmin); - - // Deploy a upgradeable account to the counterfactual address - vm.prank(factoryAdmin); - recoverableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); - - // Calling it again should just return the address and not create another account - vm.prank(factoryAdmin); - recoverableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); - - // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); - assertEq(accountAddress2, recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, "2")); - } - - /* - * Create an account calling the factory via EntryPoint. - * Use initCode - */ - function testCreateAccountViaEntryPoint() public { - // It is not correct to use the Factory using the EntryPoint anymore - // Accounts created using factories are depend on msg.sender now - // revert(); - - // Make sure the smart account does not have any code yet - address account2 = recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); - assertEq(account2.code.length, 0); - - bytes memory initCallData = - abi.encodeWithSignature("createAccountWithNonce(address,bytes32)", accountAdmin, bytes32("2")); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(recoverableOpenfortFactory)), initCallData); - - UserOperation[] memory userOpCreateAccount = - _setupUserOpExecute(account2, accountAdminPKey, initCode, address(0), 0, bytes("")); - - // Expect that we will see an event containing the account and admin - vm.expectEmit(true, true, false, true); - emit AccountCreated(account2, accountAdmin); - - entryPoint.handleOps(userOpCreateAccount, beneficiary); - - // Make sure the smart account does have some code now - assert(account2.code.length > 0); - - // Make sure the counterfactual address has not been altered - assertEq(account2, recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); - } - - /* - * Create an account using the factory and make it call count() directly. - */ - function testIncrementCounterDirect() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - // Make the admin of the upgradeable account wallet (deployer) call "count" - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).execute( - address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Create an account by directly calling the factory and make it call count() - * using the execute() function using the EntryPoint (userOp). Leaveraging ERC-4337. - */ - function testIncrementCounterViaEntrypoint() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Create an account by directly calling the factory and make it call count() - * using the executeBatching() function using the EntryPoint (userOp). Leaveraging ERC-4337. - */ - function testIncrementCounterViaEntrypointBatching() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 3); - } - - /* - * Should fail, try to use a sessionKey that is not registered. - */ - function testFailIncrementCounterViaSessionKeyNotregistered() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Use a sessionKey that is registered. - */ - function testIncrementCounterViaSessionKey() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - address[] memory emptyWhitelist; - - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Register a sessionKey via userOp calling the execute() function - * using the EntryPoint (userOp). Then use the sessionKey to count - */ - function testRegisterSessionKeyViaEntrypoint() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - address[] memory emptyWhitelist; - - UserOperation[] memory userOp = _setupUserOp( - account, - accountAdminPKey, - bytes(""), - abi.encodeWithSignature( - "registerSessionKey(address,uint48,uint48,uint48,address[])", - sessionKey, - 0, - 2 ** 48 - 1, - 100, - emptyWhitelist - ) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - - userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Register a master sessionKey via userOp calling the execute() function - * using the EntryPoint (userOp). Then use that sessionKey to register a second one - * Should not be allowed: session keys cannot register new session keys! - */ - function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - address[] memory emptyWhitelist; - - UserOperation[] memory userOp = _setupUserOp( - account, - accountAdminPKey, - bytes(""), - abi.encodeWithSignature( - "registerSessionKey(address,uint48,uint48,uint48,address[])", - sessionKey, - 0, - 2 ** 48 - 1, - 2 ** 48 - 1, - emptyWhitelist - ) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - - userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - - address sessionKeyAttack; - uint256 sessionKeyPrivKeyAttack; - (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); - - userOp = _setupUserOp( - account, - sessionKeyPrivKey, - bytes(""), - abi.encodeWithSignature( - "registerSessionKey(address,uint48,uint48,uint48,address[])", - sessionKeyAttack, - 0, - 2 ** 48 - 1, - 2 ** 48 - 1, - emptyWhitelist - ) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - } - - /* - * Should fail, try to use a sessionKey that is expired. - */ - function testIncrementCounterViaSessionKeyExpired() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - address[] memory emptyWhitelist; - - vm.warp(100); - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - vm.expectRevert(); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Should fail, try to use a sessionKey that is revoked. - */ - function testFailIncrementCounterViaSessionKeyRevoked() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - address[] memory emptyWhitelist; - - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); - RecoverableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Should fail, try to use a sessionKey that reached its limit. - */ - function testFailIncrementCounterViaSessionKeyReachLimit() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - address[] memory emptyWhitelist; - - // We are now in block 100, but our session key is valid until block 150 - vm.warp(100); - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has only increased by one - assertEq(testCounter.counters(account), 1); - } - - /* - * Should fail, try to use a sessionKey that reached its limit. - */ - function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - address[] memory emptyWhitelist; - - // We are now in block 100, but our session key is valid until block 150 - vm.warp(100); - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Should fail, try to revoke a sessionKey using a non-privileged user - */ - function testFailRevokeSessionKeyInvalidUser() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - address[] memory emptyWhitelist; - - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); - vm.prank(beneficiary); - RecoverableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Use a sessionKey with whitelisting to call Execute(). - */ - function testIncrementCounterViaSessionKeyWhitelisting() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](1); - whitelist[0] = address(testCounter); - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Should fail, try to register a sessionKey with a large whitelist. - */ - function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](11); - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Use a sessionKey with whitelisting to call ExecuteBatch(). - */ - function testIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](1); - whitelist[0] = address(testCounter); - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); - - // Verify that the registered key is not a MasterKey but has whitelisting - bool isMasterKey; - bool isWhitelisted; - (,,, isMasterKey, isWhitelisted,) = RecoverableOpenfortAccount(payable(account)).sessionKeys(sessionKey); - assert(!isMasterKey); - assert(isWhitelisted); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 3); - } - - /* - * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). - */ - function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](1); - whitelist[0] = address(account); - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Should fail, try to use a sessionKey with invalid whitelisting to call ExecuteBatch(). - */ - function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - address sessionKey; - uint256 sessionKeyPrivKey; - (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - address[] memory whitelist = new address[](1); - whitelist[0] = address(account); - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(testCounter); - values[i] = 0; - callData[i] = abi.encodeWithSignature("count()"); - } - - UserOperation[] memory userOp = _setupUserOpExecuteBatch( - account, - sessionKeyPrivKey, //Sign the userOp using the sessionKey's private key - bytes(""), - targets, - values, - callData - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Change the owner of an account and call TestCounter directly. - * Important use-case: - * 1- accountAdmin is Openfort's master wallet and is managing the account of the user. - * 2- The user claims the ownership of the account to Openfort so Openfort calls - * transferOwnership() to the account. - * 3- The user has to "officially" claim the ownership of the account by directly - * interacting with the smart contract using the acceptOwnership() function. - * 4- From now on, the user is the owner of the account and can register and revoke session keys themselves. - * 5- Test that the new owner can directly interact with the account and make it call the testCounter contract. - */ - function testChangeOwnershipAndCountDirect() public { - address accountAdmin2; - uint256 accountAdmin2PKey; - (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); - - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); - vm.prank(accountAdmin2); - RecoverableOpenfortAccount(payable(account)).acceptOwnership(); - - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - // Make the admin of the upgradeable account wallet (deployer) call "count" - vm.prank(accountAdmin2); - RecoverableOpenfortAccount(payable(account)).execute( - address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Change the owner of an account and call TestCounter though the Entrypoint - */ - function testChangeOwnershipAndCountEntryPoint() public { - address accountAdmin2; - uint256 accountAdmin2PKey; - (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); - - vm.prank(accountAdmin); - RecoverableOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); - vm.prank(accountAdmin2); - RecoverableOpenfortAccount(payable(account)).acceptOwnership(); - - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Test an account with testToken instead of TestCount. - */ - function testMintTokenAccount() public { - // Verify that the totalSupply is stil 0 - assertEq(testToken.totalSupply(), 0); - - // Mint 1 to beneficiary - testToken.mint(beneficiary, 1); - assertEq(testToken.totalSupply(), 1); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - entryPoint.handleOps(userOp, beneficiary); - - // Verify that the totalSupply has increased - assertEq(testToken.totalSupply(), 2); - } - - /* - * Test receive native tokens. - */ - function testReceiveNativeToken() public { - assertEq(address(account).balance, 0); - - vm.prank(accountAdmin); - (bool success,) = payable(account).call{value: 1000}(""); - assert(success); - assertEq(address(account).balance, 1000); - } - - /* - * Transfer native tokens out of an account. - */ - function testTransferOutNativeToken() public { - uint256 value = 1000; - - assertEq(address(account).balance, 0); - vm.prank(accountAdmin); - (bool success,) = payable(account).call{value: value}(""); - assertEq(address(account).balance, value); - assert(success); - assertEq(beneficiary.balance, 0); - - UserOperation[] memory userOp = - _setupUserOpExecute(account, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); - - EntryPoint(entryPoint).handleOps(userOp, beneficiary); - assertEq(beneficiary.balance, value); - } - - /* - * Basic test of simulateValidation() to check that it always reverts. - */ - function testSimulateValidation() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); - - UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 1000000000000000000}(account); - // Expect the simulateValidation() to always revert - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * 1- Deploy a factory using the old EntryPoint to create an account. - * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() - */ - function testUpdateEntryPoint() public { - address oldEntryPoint = address(0x0576a174D229E3cFA37253523E645A78A0C91B57); - address newEntryPoint = vm.envAddress("ENTRY_POINT_ADDRESS"); - RecoverableOpenfortFactory recoverableOpenfortFactoryOld = new RecoverableOpenfortFactory( - payable(oldEntryPoint), - address(recoverableOpenfortAccountImpl), - RECOVERY_PERIOD, - SECURITY_PERIOD, - SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN - ); - - // Create an upgradeable account wallet using the old EntryPoint and get its address - address payable accountOld = payable(recoverableOpenfortFactoryOld.createAccountWithNonce(accountAdmin, "999")); - RecoverableOpenfortAccount upgradeableAccount = RecoverableOpenfortAccount(accountOld); - assertEq(address(upgradeableAccount.entryPoint()), oldEntryPoint); - - vm.expectRevert("Ownable: caller is not the owner"); - upgradeableAccount.updateEntryPoint(newEntryPoint); - - vm.expectRevert(ZeroAddressNotAllowed.selector); - vm.prank(accountAdmin); - upgradeableAccount.updateEntryPoint(address(0)); - - vm.prank(accountAdmin); - upgradeableAccount.updateEntryPoint(newEntryPoint); - - assertEq(address(upgradeableAccount.entryPoint()), newEntryPoint); - } - - /** - * Lock tests * - */ - - /* - * Test locking the Openfort account using the default guardian. - */ - function testLockAccount() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - assertEq(recoverableOpenfortAccount.isLocked(), false); - assertEq(recoverableOpenfortAccount.getLock(), 0); - - vm.expectRevert(MustBeGuardian.selector); - recoverableOpenfortAccount.lock(); - - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.lock(); - - assertEq(recoverableOpenfortAccount.isLocked(), true); - assertEq(recoverableOpenfortAccount.getLock(), block.timestamp + LOCK_PERIOD); - - vm.expectRevert(AccountLocked.selector); - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.lock(); - - // Automatically unlock - skip(LOCK_PERIOD + 1); - assertEq(recoverableOpenfortAccount.isLocked(), false); - assertEq(recoverableOpenfortAccount.getLock(), 0); - } - - /* - * Test unlocking the Openfort account using the default guardian. - */ - function testUnlockAccount() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); +// import {Test, console} from "lib/forge-std/src/Test.sol"; +// import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +// import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; +// import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +// import {TestCounter} from "account-abstraction/test/TestCounter.sol"; +// import {TestToken} from "account-abstraction/test/TestToken.sol"; +// import {RecoverableOpenfortAccount} from "contracts/core/recoverable/RecoverableOpenfortAccount.sol"; +// import {RecoverableOpenfortFactory} from "contracts/core/recoverable/RecoverableOpenfortFactory.sol"; +// import {OpenfortRecoverableProxy} from "contracts/core/recoverable/OpenfortRecoverableProxy.sol"; + +// contract RecoverableOpenfortAccountTest is Test { +// using ECDSA for bytes32; + +// EntryPoint public entryPoint; +// RecoverableOpenfortAccount public recoverableOpenfortAccountImpl; +// RecoverableOpenfortFactory public recoverableOpenfortFactory; +// address public account; +// TestCounter public testCounter; +// TestToken public testToken; + +// // Testing addresses +// address private factoryAdmin; +// uint256 private factoryAdminPKey; + +// address private accountAdmin; +// uint256 private accountAdminPKey; + +// address payable private beneficiary = payable(makeAddr("beneficiary")); + +// uint256 private constant RECOVERY_PERIOD = 36; +// uint256 private constant SECURITY_PERIOD = 24; +// uint256 private constant SECURITY_WINDOW = 12; +// uint256 private constant LOCK_PERIOD = 50; +// address private OPENFORT_GUARDIAN; +// uint256 private OPENFORT_GUARDIAN_PKEY; + +// event AccountCreated(address indexed account, address indexed accountAdmin); +// event GuardianProposed(address indexed guardian, uint256 executeAfter); +// event GuardianProposalCancelled(address indexed guardian); +// event GuardianRevocationRequested(address indexed guardian, uint256 executeAfter); +// event GuardianRevocationCancelled(address indexed guardian); + +// error ZeroAddressNotAllowed(); +// error AccountLocked(); +// error AccountNotLocked(); +// error MustBeGuardian(); +// error DuplicatedGuardian(); +// error UnknownProposal(); +// error PendingProposalNotOver(); +// error PendingProposalExpired(); +// error DuplicatedRevoke(); +// error UnknownRevoke(); +// error PendingRevokeNotOver(); +// error PendingRevokeExpired(); +// error GuardianCannotBeOwner(); +// error NoOngoingRecovery(); +// error OngoingRecovery(); +// error InvalidRecoverySignatures(); +// error InvalidSignatureAmount(); + +// // keccak256("Recover(address recoveryAddress,uint64 executeAfter,uint32 guardiansRequired)"); +// bytes32 private RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; +// bytes32 private constant _TYPE_HASH = +// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + +// /* +// * Auxiliary function to generate a userOP +// */ +// function _setupUserOp( +// address sender, +// uint256 _signerPKey, +// bytes memory _initCode, +// bytes memory _callDataForEntrypoint +// ) internal returns (UserOperation[] memory ops) { +// uint256 nonce = entryPoint.getNonce(sender, 0); + +// // Get user op fields +// UserOperation memory op = UserOperation({ +// sender: sender, +// nonce: nonce, +// initCode: _initCode, +// callData: _callDataForEntrypoint, +// callGasLimit: 500_000, +// verificationGasLimit: 500_000, +// preVerificationGas: 500_000, +// maxFeePerGas: 0, +// maxPriorityFeePerGas: 0, +// paymasterAndData: bytes(""), +// signature: bytes("") +// }); + +// // Sign UserOp +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); +// bytes memory userOpSignature = abi.encodePacked(r, s, v); + +// address recoveredSigner = ECDSA.recover(msgHash, v, r, s); +// address expectedSigner = vm.addr(_signerPKey); +// assertEq(recoveredSigner, expectedSigner); + +// op.signature = userOpSignature; + +// // Store UserOp +// ops = new UserOperation[](1); +// ops[0] = op; +// } + +// /* +// * Auxiliary function to generate a userOP using the execute() +// * from the account +// */ +// function _setupUserOpExecute( +// address sender, +// uint256 _signerPKey, +// bytes memory _initCode, +// address _target, +// uint256 _value, +// bytes memory _callData +// ) internal returns (UserOperation[] memory) { +// bytes memory callDataForEntrypoint = +// abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); + +// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); +// } + +// /* +// * Auxiliary function to generate a userOP using the executeBatch() +// * from the account +// */ +// function _setupUserOpExecuteBatch( +// address sender, +// uint256 _signerPKey, +// bytes memory _initCode, +// address[] memory _target, +// uint256[] memory _value, +// bytes[] memory _callData +// ) internal returns (UserOperation[] memory) { +// bytes memory callDataForEntrypoint = +// abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); + +// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); +// } + +// /** +// * @notice Initialize the RecoverableOpenfortAccount testing contract. +// * Scenario: +// * - factoryAdmin is the deployer (and owner) of the RecoverableOpenfortFactory +// * - accountAdmin is the account used to deploy new upgradeable accounts +// * - entryPoint is the singleton EntryPoint +// * - testCounter is the counter used to test userOps +// */ +// function setUp() public { +// // Setup and fund signers +// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); +// vm.deal(factoryAdmin, 100 ether); +// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); +// vm.deal(accountAdmin, 100 ether); + +// vm.startPrank(factoryAdmin); +// // If we are in a fork +// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { +// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); +// } +// // If not a fork, deploy entryPoint (at correct address) +// else { +// EntryPoint entryPoint_aux = new EntryPoint(); +// bytes memory code = address(entryPoint_aux).code; +// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); +// vm.etch(targetAddr, code); +// entryPoint = EntryPoint(payable(targetAddr)); +// } +// (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); +// // deploy upgradeable account implementation +// recoverableOpenfortAccountImpl = new RecoverableOpenfortAccount(); +// // deploy upgradeable account factory +// recoverableOpenfortFactory = new RecoverableOpenfortFactory( +// payable(address(entryPoint)), +// address(recoverableOpenfortAccountImpl), +// RECOVERY_PERIOD, +// SECURITY_PERIOD, +// SECURITY_WINDOW, +// LOCK_PERIOD, +// OPENFORT_GUARDIAN +// ); + +// // Create an upgradeable account wallet and get its address +// account = recoverableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); + +// // deploy a new TestCounter +// testCounter = new TestCounter(); +// // deploy a new TestToken (ERC20) +// testToken = new TestToken(); +// vm.stopPrank(); +// } + +// function testCreateWrongFactory() public { +// vm.expectRevert(ZeroAddressNotAllowed.selector); +// vm.prank(factoryAdmin); +// recoverableOpenfortFactory = new RecoverableOpenfortFactory( +// payable(address(entryPoint)), +// address(0), +// RECOVERY_PERIOD, +// SECURITY_PERIOD, +// SECURITY_WINDOW, +// LOCK_PERIOD, +// OPENFORT_GUARDIAN +// ); +// } + +// function testCreateWrongAccount() public { +// vm.expectRevert(ZeroAddressNotAllowed.selector); +// vm.prank(accountAdmin); +// account = address( +// new OpenfortRecoverableProxy{salt: 0}( +// address(recoverableOpenfortAccountImpl), +// abi.encodeCall( +// RecoverableOpenfortAccount.initialize, +// ( +// address(0x0), +// address(entryPoint), +// RECOVERY_PERIOD, +// SECURITY_PERIOD, +// SECURITY_WINDOW, +// LOCK_PERIOD, +// OPENFORT_GUARDIAN) +// ) +// ) +// ); +// } + +// /* +// * Create an account by directly calling the factory. +// */ +// function testCreateAccountWithNonceViaFactory() public { +// // Get the counterfactual address +// vm.prank(factoryAdmin); +// address accountAddress2 = recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, "2"); + +// // Expect that we will see an event containing the account and admin +// vm.expectEmit(true, true, false, true); +// emit AccountCreated(accountAddress2, accountAdmin); + +// // Deploy a upgradeable account to the counterfactual address +// vm.prank(factoryAdmin); +// recoverableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); + +// // Calling it again should just return the address and not create another account +// vm.prank(factoryAdmin); +// recoverableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); + +// // Make sure the counterfactual address has not been altered +// vm.prank(factoryAdmin); +// assertEq(accountAddress2, recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, "2")); +// } + +// /* +// * Create an account calling the factory via EntryPoint. +// * Use initCode +// */ +// function testCreateAccountViaEntryPoint() public { +// // It is not correct to use the Factory using the EntryPoint anymore +// // Accounts created using factories are depend on msg.sender now +// // revert(); + +// // Make sure the smart account does not have any code yet +// address account2 = recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); +// assertEq(account2.code.length, 0); + +// bytes memory initCallData = +// abi.encodeWithSignature("createAccountWithNonce(address,bytes32)", accountAdmin, bytes32("2")); +// bytes memory initCode = abi.encodePacked(abi.encodePacked(address(recoverableOpenfortFactory)), initCallData); + +// UserOperation[] memory userOpCreateAccount = +// _setupUserOpExecute(account2, accountAdminPKey, initCode, address(0), 0, bytes("")); + +// // Expect that we will see an event containing the account and admin +// vm.expectEmit(true, true, false, true); +// emit AccountCreated(account2, accountAdmin); + +// entryPoint.handleOps(userOpCreateAccount, beneficiary); + +// // Make sure the smart account does have some code now +// assert(account2.code.length > 0); + +// // Make sure the counterfactual address has not been altered +// assertEq(account2, recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); +// } + +// /* +// * Create an account using the factory and make it call count() directly. +// */ +// function testIncrementCounterDirect() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// // Make the admin of the upgradeable account wallet (deployer) call "count" +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).execute( +// address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Create an account by directly calling the factory and make it call count() +// * using the execute() function using the EntryPoint (userOp). Leaveraging ERC-4337. +// */ +// function testIncrementCounterViaEntrypoint() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Create an account by directly calling the factory and make it call count() +// * using the executeBatching() function using the EntryPoint (userOp). Leaveraging ERC-4337. +// */ +// function testIncrementCounterViaEntrypointBatching() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// uint256 count = 3; +// address[] memory targets = new address[](count); +// uint256[] memory values = new uint256[](count); +// bytes[] memory callData = new bytes[](count); + +// for (uint256 i = 0; i < count; i += 1) { +// targets[i] = address(testCounter); +// values[i] = 0; +// callData[i] = abi.encodeWithSignature("count()"); +// } + +// UserOperation[] memory userOp = +// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 3); +// } + +// /* +// * Should fail, try to use a sessionKey that is not registered. +// */ +// function testFailIncrementCounterViaSessionKeyNotregistered() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); +// } + +// /* +// * Use a sessionKey that is registered. +// */ +// function testIncrementCounterViaSessionKey() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); +// address[] memory emptyWhitelist; + +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Register a sessionKey via userOp calling the execute() function +// * using the EntryPoint (userOp). Then use the sessionKey to count +// */ +// function testRegisterSessionKeyViaEntrypoint() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); +// address[] memory emptyWhitelist; + +// UserOperation[] memory userOp = _setupUserOp( +// account, +// accountAdminPKey, +// bytes(""), +// abi.encodeWithSignature( +// "registerSessionKey(address,uint48,uint48,uint48,address[])", +// sessionKey, +// 0, +// 2 ** 48 - 1, +// 100, +// emptyWhitelist +// ) +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); + +// userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Register a master sessionKey via userOp calling the execute() function +// * using the EntryPoint (userOp). Then use that sessionKey to register a second one +// * Should not be allowed: session keys cannot register new session keys! +// */ +// function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); +// address[] memory emptyWhitelist; + +// UserOperation[] memory userOp = _setupUserOp( +// account, +// accountAdminPKey, +// bytes(""), +// abi.encodeWithSignature( +// "registerSessionKey(address,uint48,uint48,uint48,address[])", +// sessionKey, +// 0, +// 2 ** 48 - 1, +// 2 ** 48 - 1, +// emptyWhitelist +// ) +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); + +// userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); + +// address sessionKeyAttack; +// uint256 sessionKeyPrivKeyAttack; +// (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); + +// userOp = _setupUserOp( +// account, +// sessionKeyPrivKey, +// bytes(""), +// abi.encodeWithSignature( +// "registerSessionKey(address,uint48,uint48,uint48,address[])", +// sessionKeyAttack, +// 0, +// 2 ** 48 - 1, +// 2 ** 48 - 1, +// emptyWhitelist +// ) +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); +// } + +// /* +// * Should fail, try to use a sessionKey that is expired. +// */ +// function testIncrementCounterViaSessionKeyExpired() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); +// address[] memory emptyWhitelist; + +// vm.warp(100); +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// vm.expectRevert(); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); +// } + +// /* +// * Should fail, try to use a sessionKey that is revoked. +// */ +// function testFailIncrementCounterViaSessionKeyRevoked() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); +// address[] memory emptyWhitelist; + +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); +// RecoverableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Should fail, try to use a sessionKey that reached its limit. +// */ +// function testFailIncrementCounterViaSessionKeyReachLimit() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); +// address[] memory emptyWhitelist; + +// // We are now in block 100, but our session key is valid until block 150 +// vm.warp(100); +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has only increased by one +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Should fail, try to use a sessionKey that reached its limit. +// */ +// function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); +// address[] memory emptyWhitelist; + +// // We are now in block 100, but our session key is valid until block 150 +// vm.warp(100); +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); + +// uint256 count = 3; +// address[] memory targets = new address[](count); +// uint256[] memory values = new uint256[](count); +// bytes[] memory callData = new bytes[](count); + +// for (uint256 i = 0; i < count; i += 1) { +// targets[i] = address(testCounter); +// values[i] = 0; +// callData[i] = abi.encodeWithSignature("count()"); +// } + +// UserOperation[] memory userOp = +// _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); +// } + +// /* +// * Should fail, try to revoke a sessionKey using a non-privileged user +// */ +// function testFailRevokeSessionKeyInvalidUser() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); +// address[] memory emptyWhitelist; + +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); +// vm.prank(beneficiary); +// RecoverableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Use a sessionKey with whitelisting to call Execute(). +// */ +// function testIncrementCounterViaSessionKeyWhitelisting() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + +// address[] memory whitelist = new address[](1); +// whitelist[0] = address(testCounter); +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Should fail, try to register a sessionKey with a large whitelist. +// */ +// function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + +// address[] memory whitelist = new address[](11); +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); +// } + +// /* +// * Use a sessionKey with whitelisting to call ExecuteBatch(). +// */ +// function testIncrementCounterViaSessionKeyWhitelistingBatch() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + +// address[] memory whitelist = new address[](1); +// whitelist[0] = address(testCounter); +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); + +// // Verify that the registered key is not a MasterKey but has whitelisting +// bool isMasterKey; +// bool isWhitelisted; +// (,,, isMasterKey, isWhitelisted,) = RecoverableOpenfortAccount(payable(account)).sessionKeys(sessionKey); +// assert(!isMasterKey); +// assert(isWhitelisted); + +// uint256 count = 3; +// address[] memory targets = new address[](count); +// uint256[] memory values = new uint256[](count); +// bytes[] memory callData = new bytes[](count); + +// for (uint256 i = 0; i < count; i += 1) { +// targets[i] = address(testCounter); +// values[i] = 0; +// callData[i] = abi.encodeWithSignature("count()"); +// } + +// UserOperation[] memory userOp = +// _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 3); +// } + +// /* +// * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). +// */ +// function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + +// address[] memory whitelist = new address[](1); +// whitelist[0] = address(account); +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Should fail, try to use a sessionKey with invalid whitelisting to call ExecuteBatch(). +// */ +// function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// address sessionKey; +// uint256 sessionKeyPrivKey; +// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + +// address[] memory whitelist = new address[](1); +// whitelist[0] = address(account); +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + +// uint256 count = 3; +// address[] memory targets = new address[](count); +// uint256[] memory values = new uint256[](count); +// bytes[] memory callData = new bytes[](count); + +// for (uint256 i = 0; i < count; i += 1) { +// targets[i] = address(testCounter); +// values[i] = 0; +// callData[i] = abi.encodeWithSignature("count()"); +// } + +// UserOperation[] memory userOp = _setupUserOpExecuteBatch( +// account, +// sessionKeyPrivKey, //Sign the userOp using the sessionKey's private key +// bytes(""), +// targets, +// values, +// callData +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); +// } + +// /* +// * Change the owner of an account and call TestCounter directly. +// * Important use-case: +// * 1- accountAdmin is Openfort's master wallet and is managing the account of the user. +// * 2- The user claims the ownership of the account to Openfort so Openfort calls +// * transferOwnership() to the account. +// * 3- The user has to "officially" claim the ownership of the account by directly +// * interacting with the smart contract using the acceptOwnership() function. +// * 4- From now on, the user is the owner of the account and can register and revoke session keys themselves. +// * 5- Test that the new owner can directly interact with the account and make it call the testCounter contract. +// */ +// function testChangeOwnershipAndCountDirect() public { +// address accountAdmin2; +// uint256 accountAdmin2PKey; +// (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); + +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); +// vm.prank(accountAdmin2); +// RecoverableOpenfortAccount(payable(account)).acceptOwnership(); + +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// // Make the admin of the upgradeable account wallet (deployer) call "count" +// vm.prank(accountAdmin2); +// RecoverableOpenfortAccount(payable(account)).execute( +// address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Change the owner of an account and call TestCounter though the Entrypoint +// */ +// function testChangeOwnershipAndCountEntryPoint() public { +// address accountAdmin2; +// uint256 accountAdmin2PKey; +// (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); + +// vm.prank(accountAdmin); +// RecoverableOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); +// vm.prank(accountAdmin2); +// RecoverableOpenfortAccount(payable(account)).acceptOwnership(); + +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Test an account with testToken instead of TestCount. +// */ +// function testMintTokenAccount() public { +// // Verify that the totalSupply is stil 0 +// assertEq(testToken.totalSupply(), 0); + +// // Mint 1 to beneficiary +// testToken.mint(beneficiary, 1); +// assertEq(testToken.totalSupply(), 1); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testToken), +// 0, +// abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); +// entryPoint.handleOps(userOp, beneficiary); + +// // Verify that the totalSupply has increased +// assertEq(testToken.totalSupply(), 2); +// } + +// /* +// * Test receive native tokens. +// */ +// function testReceiveNativeToken() public { +// assertEq(address(account).balance, 0); + +// vm.prank(accountAdmin); +// (bool success,) = payable(account).call{value: 1000}(""); +// assert(success); +// assertEq(address(account).balance, 1000); +// } + +// /* +// * Transfer native tokens out of an account. +// */ +// function testTransferOutNativeToken() public { +// uint256 value = 1000; + +// assertEq(address(account).balance, 0); +// vm.prank(accountAdmin); +// (bool success,) = payable(account).call{value: value}(""); +// assertEq(address(account).balance, value); +// assert(success); +// assertEq(beneficiary.balance, 0); + +// UserOperation[] memory userOp = +// _setupUserOpExecute(account, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); + +// EntryPoint(entryPoint).handleOps(userOp, beneficiary); +// assertEq(beneficiary.balance, value); +// } + +// /* +// * Basic test of simulateValidation() to check that it always reverts. +// */ +// function testSimulateValidation() public { +// // Verify that the counter is stil set to 0 +// assertEq(testCounter.counters(account), 0); + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") +// ); + +// entryPoint.depositTo{value: 1000000000000000000}(account); +// // Expect the simulateValidation() to always revert +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); +// } + +// /* +// * 1- Deploy a factory using the old EntryPoint to create an account. +// * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() +// */ +// function testUpdateEntryPoint() public { +// address oldEntryPoint = address(0x0576a174D229E3cFA37253523E645A78A0C91B57); +// address newEntryPoint = vm.envAddress("ENTRY_POINT_ADDRESS"); +// RecoverableOpenfortFactory recoverableOpenfortFactoryOld = new RecoverableOpenfortFactory( +// payable(oldEntryPoint), +// address(recoverableOpenfortAccountImpl), +// RECOVERY_PERIOD, +// SECURITY_PERIOD, +// SECURITY_WINDOW, +// LOCK_PERIOD, +// OPENFORT_GUARDIAN +// ); + +// // Create an upgradeable account wallet using the old EntryPoint and get its address +// address payable accountOld = payable(recoverableOpenfortFactoryOld.createAccountWithNonce(accountAdmin, "999")); +// RecoverableOpenfortAccount upgradeableAccount = RecoverableOpenfortAccount(accountOld); +// assertEq(address(upgradeableAccount.entryPoint()), oldEntryPoint); + +// vm.expectRevert("Ownable: caller is not the owner"); +// upgradeableAccount.updateEntryPoint(newEntryPoint); + +// vm.expectRevert(ZeroAddressNotAllowed.selector); +// vm.prank(accountAdmin); +// upgradeableAccount.updateEntryPoint(address(0)); + +// vm.prank(accountAdmin); +// upgradeableAccount.updateEntryPoint(newEntryPoint); + +// assertEq(address(upgradeableAccount.entryPoint()), newEntryPoint); +// } + +// /** +// * Lock tests * +// */ + +// /* +// * Test locking the Openfort account using the default guardian. +// */ +// function testLockAccount() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// assertEq(recoverableOpenfortAccount.isLocked(), false); +// assertEq(recoverableOpenfortAccount.getLock(), 0); + +// vm.expectRevert(MustBeGuardian.selector); +// recoverableOpenfortAccount.lock(); + +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.lock(); + +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// assertEq(recoverableOpenfortAccount.getLock(), block.timestamp + LOCK_PERIOD); + +// vm.expectRevert(AccountLocked.selector); +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.lock(); + +// // Automatically unlock +// skip(LOCK_PERIOD + 1); +// assertEq(recoverableOpenfortAccount.isLocked(), false); +// assertEq(recoverableOpenfortAccount.getLock(), 0); +// } + +// /* +// * Test unlocking the Openfort account using the default guardian. +// */ +// function testUnlockAccount() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - assertEq(recoverableOpenfortAccount.isLocked(), false); - assertEq(recoverableOpenfortAccount.getLock(), 0); - - vm.expectRevert(MustBeGuardian.selector); - recoverableOpenfortAccount.lock(); - - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.lock(); - - assertEq(recoverableOpenfortAccount.isLocked(), true); - assertEq(recoverableOpenfortAccount.getLock(), block.timestamp + LOCK_PERIOD); +// assertEq(recoverableOpenfortAccount.isLocked(), false); +// assertEq(recoverableOpenfortAccount.getLock(), 0); + +// vm.expectRevert(MustBeGuardian.selector); +// recoverableOpenfortAccount.lock(); + +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.lock(); + +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// assertEq(recoverableOpenfortAccount.getLock(), block.timestamp + LOCK_PERIOD); - skip(LOCK_PERIOD / 2); +// skip(LOCK_PERIOD / 2); - vm.expectRevert(MustBeGuardian.selector); - recoverableOpenfortAccount.unlock(); - assertEq(recoverableOpenfortAccount.isLocked(), true); +// vm.expectRevert(MustBeGuardian.selector); +// recoverableOpenfortAccount.unlock(); +// assertEq(recoverableOpenfortAccount.isLocked(), true); - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.unlock(); +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.unlock(); - assertEq(recoverableOpenfortAccount.isLocked(), false); - assertEq(recoverableOpenfortAccount.getLock(), 0); +// assertEq(recoverableOpenfortAccount.isLocked(), false); +// assertEq(recoverableOpenfortAccount.getLock(), 0); - vm.expectRevert(AccountNotLocked.selector); - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.unlock(); - } +// vm.expectRevert(AccountNotLocked.selector); +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.unlock(); +// } - /** - * Add guardians tests * - */ +// /** +// * Add guardians tests * +// */ - /* - * Test proposing a guardian (by the owner) and accepting it (by the owner). - * Successfully propose a guardian and confirm it after SECURITY_PERIOD - */ - function testAddEOAGuardian() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); +// /* +// * Test proposing a guardian (by the owner) and accepting it (by the owner). +// * Successfully propose a guardian and confirm it after SECURITY_PERIOD +// */ +// function testAddEOAGuardian() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - recoverableOpenfortAccount.getGuardians(); +// recoverableOpenfortAccount.getGuardians(); - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Create a friend EOA - address friendAccount = makeAddr("friend"); +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.proposeGuardian(friendAccount); +// // Trying to proposa a guardian not using the owner +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - // Test if zero address is a guardian - assertEq(recoverableOpenfortAccount.isGuardian(address(0)), false); +// // Test if zero address is a guardian +// assertEq(recoverableOpenfortAccount.isGuardian(address(0)), false); - skip(1); +// skip(1); - vm.expectRevert(PendingProposalNotOver.selector); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// vm.expectRevert(PendingProposalNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - // Verify that the number of guardians is now 2 - assertEq(recoverableOpenfortAccount.guardianCount(), 2); +// // Verify that the number of guardians is now 2 +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - // Friend account should be a guardian now - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - } +// // Friend account should be a guardian now +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); +// } - /* - * Test proposing a guardian, but its proposal expires before accepting. - * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. - */ - function testAddEOAGuardianExpired() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); +// /* +// * Test proposing a guardian, but its proposal expires before accepting. +// * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. +// */ +// function testAddEOAGuardianExpired() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Create a friend EOA - address friendAccount = makeAddr("friend"); +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.proposeGuardian(friendAccount); +// // Trying to proposa a guardian not using the owner +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - skip(1); +// skip(1); - vm.expectRevert(PendingProposalNotOver.selector); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// vm.expectRevert(PendingProposalNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - skip(SECURITY_PERIOD + SECURITY_WINDOW); - vm.expectRevert(PendingProposalExpired.selector); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// skip(SECURITY_PERIOD + SECURITY_WINDOW); +// vm.expectRevert(PendingProposalExpired.selector); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - } +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// } - /* - * Test proposing a guardian, but its proposal expires before accepting. Re-add again - * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. - */ - function testAddEOAGuardianExpiredThenReAdd() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); +// /* +// * Test proposing a guardian, but its proposal expires before accepting. Re-add again +// * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. +// */ +// function testAddEOAGuardianExpiredThenReAdd() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Create a friend EOA - address friendAccount = makeAddr("friend"); +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.proposeGuardian(friendAccount); +// // Trying to proposa a guardian not using the owner +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - skip(1); +// skip(1); - vm.expectRevert(PendingProposalNotOver.selector); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// vm.expectRevert(PendingProposalNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - skip(SECURITY_PERIOD + SECURITY_WINDOW); - vm.expectRevert(PendingProposalExpired.selector); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// skip(SECURITY_PERIOD + SECURITY_WINDOW); +// vm.expectRevert(PendingProposalExpired.selector); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - /* Let's try it again (re-add) */ - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); +// /* Let's try it again (re-add) */ +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - // Verify that the number of guardians is now 2 - assertEq(recoverableOpenfortAccount.guardianCount(), 2); +// // Verify that the number of guardians is now 2 +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - } +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); +// } - /* - * Test proposing a guardian twice. Make sure a new proposal is not created and the original still works. - * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. - */ - function testAddEOAGuardianDuplicatedPorposal() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); +// /* +// * Test proposing a guardian twice. Make sure a new proposal is not created and the original still works. +// * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. +// */ +// function testAddEOAGuardianDuplicatedPorposal() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Create a friend EOA - address friendAccount = makeAddr("friend"); +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - skip(1); +// skip(1); - vm.expectRevert(); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); +// vm.expectRevert(); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); - // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - // Verify that the number of guardians is now 2 - assertEq(recoverableOpenfortAccount.guardianCount(), 2); +// // Verify that the number of guardians is now 2 +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - } +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); +// } - /* - * Test proposing a guardian and cancel its proposal before accepting or expiring - * Only the owner can cancel an ongoing proposal. - */ - function testAddEOAGuardianCancel() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); +// /* +// * Test proposing a guardian and cancel its proposal before accepting or expiring +// * Only the owner can cancel an ongoing proposal. +// */ +// function testAddEOAGuardianCancel() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Create a friend EOA - address friendAccount = makeAddr("friend"); +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - - skip(1); - vm.expectRevert(PendingProposalNotOver.selector); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - - skip(SECURITY_PERIOD); - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.cancelGuardianProposal(friendAccount); - - vm.expectEmit(true, true, false, true); - emit GuardianProposalCancelled(friendAccount); - vm.prank(accountAdmin); - recoverableOpenfortAccount.cancelGuardianProposal(friendAccount); - - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - - vm.prank(accountAdmin); - vm.expectRevert(UnknownProposal.selector); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - } - - /* - * Test proposing owner as guardian. It should revert. - * Successfully propose a guardian and confirm it after SECURITY_PERIOD - */ - function testAddOwnerAsGuardianNotAllowed() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - recoverableOpenfortAccount.getGuardians(); - - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Expect revert because the owner cannot be proposed as guardian - vm.expectRevert(); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(accountAdmin); - - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Owner account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(accountAdmin), false); - - // Expect revert because the default guardian cannot be proposed again - vm.expectRevert(DuplicatedGuardian.selector); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(OPENFORT_GUARDIAN); - - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // OPENFORT_GUARDIAN account should stil be a guardian - assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), true); - } - - /* - * Test proposing multiple guardians (by the owner) and accepting them afterwards (by the owner). - * Successfully propose guardians and confirm them after SECURITY_PERIOD - */ - function testAddMultipleEOAGuardians() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - recoverableOpenfortAccount.getGuardians(); - - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Create multiple friend EOAs - address[] memory friends = new address[](5); - friends[0] = makeAddr("friend"); - friends[1] = makeAddr("friend2"); - friends[2] = makeAddr("friend3"); - friends[3] = makeAddr("friend4"); - friends[4] = makeAddr("friend5"); - - for (uint256 index = 0; index < friends.length; index++) { - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friends[index], block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friends[index]); - } - - // Verify that the number of guardians is still 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian yet - assertEq(recoverableOpenfortAccount.isGuardian(friends[0]), false); - - skip(1); - skip(SECURITY_PERIOD); - - for (uint256 index = 0; index < friends.length; index++) { - recoverableOpenfortAccount.confirmGuardianProposal(friends[index]); - } - - // Verify that the number of guardians is now 6 - assertEq(recoverableOpenfortAccount.guardianCount(), 6); - - // First friend account should be a guardian now - assertEq(recoverableOpenfortAccount.isGuardian(friends[0]), true); - } - - /** - * Revoke guardians tests * - */ - - /* - * Test revoking a guardian using owner. - * Only the owner can revoke a guardian. - */ - function testRevokeGuardian() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Create a friend EOA - address friendAccount = makeAddr("friend"); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - - // Verify that the number of guardians is now 2 - assertEq(recoverableOpenfortAccount.guardianCount(), 2); - - // Friend account should be a guardian now - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - - // Trying to revoke a guardian not using the owner - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.revokeGuardian(friendAccount); - - // Trying to revoke a non-existen guardian (random beneficiary address) - vm.expectRevert(MustBeGuardian.selector); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(beneficiary); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(friendAccount); - - // Anyone can confirm a revokation. However, the security period has not passed yet - skip(1); - vm.expectRevert(PendingRevokeNotOver.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - - // Anyone can confirm a revokation after security period - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - - // Friend account is not a guardian anymore - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - // Verify that the number of guardians is 1 again - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - } - - /* - * Test revoking the default guardian when having registered another (custom) one. - * Only the owner can revoke a guardian. - */ - function testRevokeDefaultGuardian() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - recoverableOpenfortAccount.getGuardians(); - - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Create a friend EOA - address friendAccount = makeAddr("friend"); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - - // Verify that the number of guardians is now 2 - assertEq(recoverableOpenfortAccount.guardianCount(), 2); - - // Friend account should be a guardian now - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - - // Trying to revoke a guardian not using the owner - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - - // Anyone can confirm a revokation. However, the security period has not passed yet - skip(1); - vm.expectRevert(PendingRevokeNotOver.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - - // Anyone can confirm a revokation after security period - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - - // Default account is not a guardian anymore - assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), false); - // Verify that the number of guardians is 1 again - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - } - - /* - * Test revoking all guardians using owner. - * Only the owner can revoke a guardian. - */ - function testRevokeAllGuardians() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Create a friend EOA - address friendAccount = makeAddr("friend"); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - - // Verify that the number of guardians is now 2 - assertEq(recoverableOpenfortAccount.guardianCount(), 2); - - // Friend account should be a guardian now - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - - // Trying to revoke a guardian not using the owner - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.revokeGuardian(friendAccount); - - // Trying to revoke a non-existen guardian (random beneficiary address) - vm.expectRevert(MustBeGuardian.selector); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(beneficiary); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(friendAccount); - - // Anyone can confirm a revokation. However, the security period has not passed yet - skip(1); - vm.expectRevert(PendingRevokeNotOver.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - - // Anyone can confirm a revokation after security period - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - - // Friend account is not a guardian anymore - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - // Verify that the number of guardians is 1 again - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - - // Anyone can confirm a revokation. However, the security period has not passed yet - skip(1); - vm.expectRevert(PendingRevokeNotOver.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - - // Anyone can confirm a revokation after security period - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - - // Default account is not a guardian anymore - assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), false); - // Verify that the number of guardians is 1 again - assertEq(recoverableOpenfortAccount.guardianCount(), 0); - } - - /* - * Test revoking a guardian, but its revocation expired before confirming. - * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD + SECURITY_WINDOW. - */ - function testRevokeEOAGuardianExpired() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Create a friend EOA - address friendAccount = makeAddr("friend"); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(friendAccount); - - skip(1); - skip(SECURITY_PERIOD + SECURITY_WINDOW); - vm.expectRevert(PendingRevokeExpired.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - - // Verify that the number of guardians is still 2. No revocation took place - assertEq(recoverableOpenfortAccount.guardianCount(), 2); - - // Friend account should still be a guardian - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - } - - /* - * Test revoking a guardian twice. Make sure a new revocation is not created and the original still works. - * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD and SECURITY_WINDOW. - */ - function testRevokeEOAGuardianDuplicatedPorposal() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Create a friend EOA - address friendAccount = makeAddr("friend"); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - - // Verify that the number of guardians is now 2 - assertEq(recoverableOpenfortAccount.guardianCount(), 2); - // Friend account should now be a guardian - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) - recoverableOpenfortAccount.revokeGuardian(friendAccount); - - vm.expectRevert(DuplicatedRevoke.selector); - skip(1); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(friendAccount); - - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - - // Verify that the number of guardians is now 1 - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // Friend account should not be a guardian anymore - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - } - - /* - * Test revoking the default guardian and add it back. - */ - function testRevokeDefaultGuardianAndAddBack() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) - recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - - skip(SECURITY_PERIOD + 1); - recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - - // Verify that the number of guardians is now 0 - assertEq(recoverableOpenfortAccount.guardianCount(), 0); - // deault (openfort) account should not be a guardian anymore - assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), false); - - // Expect that we will see an event containing the deault (openfort) account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(OPENFORT_GUARDIAN); - - skip(SECURITY_PERIOD + 1); - recoverableOpenfortAccount.confirmGuardianProposal(OPENFORT_GUARDIAN); - - // Verify that the number of guardians is now 1 again - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - // deault (openfort) account should be a guardian again - assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), true); - } - - /* - * Test revoking a guardian using owner and cancel before confirming. - * Only the owner can revoke a guardian and cancel its revocation before confirming. - */ - function testCancelRevokeGuardian() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Verify that the number of guardians is 1 (default) - assertEq(recoverableOpenfortAccount.guardianCount(), 1); - - // Create a friend EOA - address friendAccount = makeAddr("friend"); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - - // Verify that the number of guardians is now 2 - assertEq(recoverableOpenfortAccount.guardianCount(), 2); - // Friend account should be a guardian now - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - - // Trying to revoke a guardian not using the owner - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.revokeGuardian(friendAccount); - - // Trying to revoke a non-existen guardian (random beneficiary address) - vm.expectRevert(MustBeGuardian.selector); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(beneficiary); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(friendAccount); - - // Anyone can confirm a revokation. However, the security period has not passed yet - skip(1); - vm.expectRevert(PendingRevokeNotOver.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.cancelGuardianRevocation(friendAccount); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianRevocationCancelled(friendAccount); - vm.prank(accountAdmin); - recoverableOpenfortAccount.cancelGuardianRevocation(friendAccount); - - // Friend account is not a guardian anymore - assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - // Verify that the number of guardians is 1 again - assertEq(recoverableOpenfortAccount.guardianCount(), 2); - - // Cancelled revocation should not be able to be confirmed now - skip(SECURITY_PERIOD); - vm.expectRevert(UnknownRevoke.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - } - - /* - * Random extra tests to mess up with the logic - */ - function testMessingUpWithGuardianRegister() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Create 4 friends - address friendAccount; - uint256 friendAccountPK; - (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - - address friendAccount2; - uint256 friendAccount2PK; - (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - - // Adding and removing guardians - vm.startPrank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - recoverableOpenfortAccount.proposeGuardian(friendAccount2); - vm.stopPrank(); - - skip(SECURITY_PERIOD + 1); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - vm.expectRevert(MustBeGuardian.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount2); // Notice this tries to confirm a non-existent revocation! - vm.expectRevert(UnknownRevoke.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); // Notice this tries to confirm a non-existent revocation! - vm.prank(accountAdmin); - vm.expectRevert(MustBeGuardian.selector); - recoverableOpenfortAccount.revokeGuardian(friendAccount2); // Notice this tries to revoke a non-existent guardian! - vm.expectRevert(DuplicatedGuardian.selector); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); // Notice this tries to register a guardian AGAIN! - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(friendAccount); // Starting a valid revocation process - skip(SECURITY_PERIOD + 1); - vm.expectRevert(DuplicatedGuardian.selector); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); // Notice this tries to confirm a guardian that is already valid and pending to revoke! - } - - /** - * Recovery tests * - */ - - /* - * Check the correct functionality of startRecovery() - */ - function testStartRecovery() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - vm.expectRevert(MustBeGuardian.selector); - recoverableOpenfortAccount.startRecovery(OPENFORT_GUARDIAN); - - vm.prank(OPENFORT_GUARDIAN); - vm.expectRevert(GuardianCannotBeOwner.selector); - recoverableOpenfortAccount.startRecovery(OPENFORT_GUARDIAN); - - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - - assertEq(recoverableOpenfortAccount.isLocked(), true); - } - - /* - * Checks that incorrect parameters should always fail when trying to complete a recovery - */ - function testBasicChecksCompleteRecovery() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - - assertEq(recoverableOpenfortAccount.isLocked(), true); - - // The recovery time period has not passed. The user should wait to recover. - vm.expectRevert(OngoingRecovery.selector); - bytes[] memory signatures = new bytes[](1); - recoverableOpenfortAccount.completeRecovery(signatures); - - // Providing an empty array when it is expecting one guardian - skip(RECOVERY_PERIOD + 1); - vm.expectRevert(InvalidSignatureAmount.selector); - bytes[] memory signatures_wrong_length = new bytes[](3); - recoverableOpenfortAccount.completeRecovery(signatures_wrong_length); - - // Since signatures are empty, it should return an ECDSA error - vm.expectRevert("ECDSA: invalid signature length"); - recoverableOpenfortAccount.completeRecovery(signatures); - } - - /* - * Auxiliary function to get a valid EIP712 signature using _eip721contract's domains separator, - * a valid hash of the message to sign (_structHash) and a private key (_pk) - */ - function getEIP712SignatureFrom(address _eip721contract, bytes32 _structHash, uint256 _pk) - internal - returns (bytes memory signature721) - { - (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = - IERC5267(_eip721contract).eip712Domain(); - bytes32 domainSeparator = keccak256( - abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) - ); - bytes32 hash712 = domainSeparator.toTypedDataHash(_structHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, hash712); - signature721 = abi.encodePacked(r, s, v); - assertEq(ecrecover(hash712, v, r, s), vm.addr(_pk)); - } - - /* - * Most basic, yet complete, recovery flow - * The default Openfort guardian is used to start and complete a recovery process. - * Ownership is transferred to beneficiary - */ - function testBasicCompleteRecovery() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - assertEq(recoverableOpenfortAccount.isLocked(), true); - - bytes32 structHash = keccak256( - abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) - ); - - bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); - - skip(RECOVERY_PERIOD + 1); - recoverableOpenfortAccount.completeRecovery(signatures); - - assertEq(recoverableOpenfortAccount.isLocked(), false); - assertEq(recoverableOpenfortAccount.owner(), address(beneficiary)); - } - - /* - * Case: User added 2 guardians and keeps the default (Openfort) - * The 2 added guardians (friends) are used to recover the account and transfer - * the ownership to beneficiary - * @notice Remember that signatures need to be ordered by the guardian's address. - */ - function test3GuardiansCompleteRecovery() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Create two friends - address friendAccount; - uint256 friendAccountPK; - (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - - address friendAccount2; - uint256 friendAccount2PK; - (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - - { - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount2); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); - } - - { - // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - assertEq(recoverableOpenfortAccount.isLocked(), true); - } - - bytes32 structHash = keccak256( - abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) - ); - - bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); - - skip(RECOVERY_PERIOD + 1); - recoverableOpenfortAccount.completeRecovery(signatures); - - assertEq(recoverableOpenfortAccount.isLocked(), false); - assertEq(recoverableOpenfortAccount.owner(), address(beneficiary)); - } - - /* - * Case: User added 2 guardians and keeps the default (Openfort) - * The 2 added guardians (friends) are used to recover the account and transfer - * the ownership to beneficiary. Faild due to unsorted signatures - * @notice Remember that signatures need to be ordered by the guardian's address. - */ - function test3GuardiansUnorderedCompleteRecovery() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Create two friends - address friendAccount; - uint256 friendAccountPK; - (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - - address friendAccount2; - uint256 friendAccount2PK; - (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - - { - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount2); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); - } - - { - // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - assertEq(recoverableOpenfortAccount.isLocked(), true); - } - - bytes32 structHash = keccak256( - abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) - ); - - bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccountPK); // Unsorted! - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); - - skip(RECOVERY_PERIOD + 1); - vm.expectRevert(InvalidRecoverySignatures.selector); - recoverableOpenfortAccount.completeRecovery(signatures); - - // it should still be locked and the admin still be the same - assertEq(recoverableOpenfortAccount.isLocked(), true); - assertEq(recoverableOpenfortAccount.owner(), accountAdmin); - } - - /* - * Case: User added 4 guardians and removes the default (Openfort) - * One guardian (friend) is used to start a recovery process - * The guardian that initiatied the recovery + another one are used to complete the flow. - * @notice Remember that signatures need to be ordered by the guardian's address. - */ - function test4GuardiansNoDefaultCompleteRecovery() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Create 4 friends - address friendAccount; - uint256 friendAccountPK; - (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - - address friendAccount2; - uint256 friendAccount2PK; - (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - - // Create 2 more friends. We don't need their PK now as they are not going to sign - address friendAccount3 = makeAddr("friend3"); - address friendAccount4 = makeAddr("friend4"); - - // Adding and removing guardians - { - vm.startPrank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - recoverableOpenfortAccount.proposeGuardian(friendAccount2); - recoverableOpenfortAccount.proposeGuardian(friendAccount3); - recoverableOpenfortAccount.proposeGuardian(friendAccount4); - vm.stopPrank(); - - skip(SECURITY_PERIOD + 1); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount3); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount4); - - vm.prank(accountAdmin); - recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - vm.expectRevert(PendingRevokeNotOver.selector); - recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - skip(SECURITY_PERIOD + 1); - recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - } - - // Start the recovery process - { - // Default Openfort guardian tries starts a recovery process because the owner lost the PK - // It should not work as it is not a guardian anymore - vm.expectRevert(MustBeGuardian.selector); - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - vm.prank(friendAccount2); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - assertEq(recoverableOpenfortAccount.isLocked(), true); - } - - bytes32 structHash = keccak256( - abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) - ); - - bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); - - skip(RECOVERY_PERIOD + 1); - recoverableOpenfortAccount.completeRecovery(signatures); - - assertEq(recoverableOpenfortAccount.isLocked(), false); - assertEq(recoverableOpenfortAccount.owner(), address(beneficiary)); - } - - /* - * Case: User added 2 guardians and keeps the default (Openfort) - * The 2 added guardians (friends) are used to recover the account and transfer - * the ownership to beneficiary. Wrong signatures used - * @notice Remember that signatures need to be ordered by the guardian's address. - */ - function test3GuardiansFailCompleteRecovery() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Create two friends - address friendAccount; - uint256 friendAccountPK; - (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - - address friendAccount2; - uint256 friendAccount2PK; - (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - - { - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount2); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); - } - - { - // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - assertEq(recoverableOpenfortAccount.isLocked(), true); - } - - // notice: wrong new oner!!! - bytes32 structHash = - keccak256(abi.encode(RECOVER_TYPEHASH, factoryAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); - - bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); - - skip(RECOVERY_PERIOD + 1); - vm.expectRevert(InvalidRecoverySignatures.selector); - recoverableOpenfortAccount.completeRecovery(signatures); - - // it should still be locked and the admin still be the same - assertEq(recoverableOpenfortAccount.isLocked(), true); - assertEq(recoverableOpenfortAccount.owner(), accountAdmin); - } - - /* - * Testing the functionality to cancel a recovery process - */ - function testCancelRecovery() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - assertEq(recoverableOpenfortAccount.isLocked(), true); - - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.cancelRecovery(); - - vm.prank(accountAdmin); - recoverableOpenfortAccount.cancelRecovery(); - - bytes32 structHash = keccak256( - abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) - ); - - bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); - - skip(RECOVERY_PERIOD + 1); - vm.expectRevert(NoOngoingRecovery.selector); - recoverableOpenfortAccount.completeRecovery(signatures); - - assertEq(recoverableOpenfortAccount.isLocked(), false); - assertEq(recoverableOpenfortAccount.owner(), accountAdmin); - } - - /* - * Testing the startRecovery twice in a row - */ - function testStartRecoveryTwice() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - assertEq(recoverableOpenfortAccount.isLocked(), true); - - // Calling startRecovery() again should revert and have no effect - vm.expectRevert(OngoingRecovery.selector); - vm.prank(OPENFORT_GUARDIAN); - recoverableOpenfortAccount.startRecovery(address(beneficiary)); - - // The accounts should still be locked - assertEq(recoverableOpenfortAccount.isLocked(), true); - assertEq(recoverableOpenfortAccount.owner(), accountAdmin); - } - - /** - * Transfer ownership tests * - */ - - /* - * Try to transfer ownership to a guardian. - * Should not be allowed. - */ - function testTransferOwnerNotGuardian() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Create a friend EOA - address friendAccount = makeAddr("friend"); - - // Expect that we will see an event containing the friend account and security period - vm.expectEmit(true, true, false, true); - emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - recoverableOpenfortAccount.proposeGuardian(friendAccount); - - skip(1); - skip(SECURITY_PERIOD); - recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - - // It should fail as friendAccount is a guardian - vm.expectRevert(GuardianCannotBeOwner.selector); - recoverableOpenfortAccount.transferOwnership(friendAccount); - } - - /* - * Try to transfer ownership to a valid account. - * Should be allowed. - */ - function testTransferOwner() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - - // Create a new owner EOA - address newOwner = makeAddr("newOwner"); - - vm.expectRevert("Ownable: caller is not the owner"); - recoverableOpenfortAccount.transferOwnership(newOwner); - - vm.prank(accountAdmin); - recoverableOpenfortAccount.transferOwnership(newOwner); - - vm.prank(newOwner); - recoverableOpenfortAccount.acceptOwnership(); - - // New owner should be now newOwner - assertEq(recoverableOpenfortAccount.owner(), address(newOwner)); - } - - /* - * Temporal test function for coverage purposes showing - * that isGuardianOrGuardianSigner() always returns false. - */ - function testStubFakeMockTempisGuardian() public { - RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - assertEq(recoverableOpenfortAccount.isGuardianOrGuardianSigner(OPENFORT_GUARDIAN), false); - } -} +// // Trying to proposa a guardian not using the owner +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); + +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); + +// skip(1); +// vm.expectRevert(PendingProposalNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); + +// skip(SECURITY_PERIOD); +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.cancelGuardianProposal(friendAccount); + +// vm.expectEmit(true, true, false, true); +// emit GuardianProposalCancelled(friendAccount); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.cancelGuardianProposal(friendAccount); + +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); + +// vm.prank(accountAdmin); +// vm.expectRevert(UnknownProposal.selector); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); + +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// } + +// /* +// * Test proposing owner as guardian. It should revert. +// * Successfully propose a guardian and confirm it after SECURITY_PERIOD +// */ +// function testAddOwnerAsGuardianNotAllowed() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// recoverableOpenfortAccount.getGuardians(); + +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Expect revert because the owner cannot be proposed as guardian +// vm.expectRevert(); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(accountAdmin); + +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Owner account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(accountAdmin), false); + +// // Expect revert because the default guardian cannot be proposed again +// vm.expectRevert(DuplicatedGuardian.selector); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // OPENFORT_GUARDIAN account should stil be a guardian +// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), true); +// } + +// /* +// * Test proposing multiple guardians (by the owner) and accepting them afterwards (by the owner). +// * Successfully propose guardians and confirm them after SECURITY_PERIOD +// */ +// function testAddMultipleEOAGuardians() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// recoverableOpenfortAccount.getGuardians(); + +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Create multiple friend EOAs +// address[] memory friends = new address[](5); +// friends[0] = makeAddr("friend"); +// friends[1] = makeAddr("friend2"); +// friends[2] = makeAddr("friend3"); +// friends[3] = makeAddr("friend4"); +// friends[4] = makeAddr("friend5"); + +// for (uint256 index = 0; index < friends.length; index++) { +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friends[index], block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friends[index]); +// } + +// // Verify that the number of guardians is still 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Friend account should not be a guardian yet +// assertEq(recoverableOpenfortAccount.isGuardian(friends[0]), false); + +// skip(1); +// skip(SECURITY_PERIOD); + +// for (uint256 index = 0; index < friends.length; index++) { +// recoverableOpenfortAccount.confirmGuardianProposal(friends[index]); +// } + +// // Verify that the number of guardians is now 6 +// assertEq(recoverableOpenfortAccount.guardianCount(), 6); + +// // First friend account should be a guardian now +// assertEq(recoverableOpenfortAccount.isGuardian(friends[0]), true); +// } + +// /** +// * Revoke guardians tests * +// */ + +// /* +// * Test revoking a guardian using owner. +// * Only the owner can revoke a guardian. +// */ +// function testRevokeGuardian() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); + +// // Verify that the number of guardians is now 2 +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); + +// // Friend account should be a guardian now +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); + +// // Trying to revoke a guardian not using the owner +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.revokeGuardian(friendAccount); + +// // Trying to revoke a non-existen guardian (random beneficiary address) +// vm.expectRevert(MustBeGuardian.selector); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(beneficiary); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(friendAccount); + +// // Anyone can confirm a revokation. However, the security period has not passed yet +// skip(1); +// vm.expectRevert(PendingRevokeNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); + +// // Anyone can confirm a revokation after security period +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); + +// // Friend account is not a guardian anymore +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// // Verify that the number of guardians is 1 again +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// } + +// /* +// * Test revoking the default guardian when having registered another (custom) one. +// * Only the owner can revoke a guardian. +// */ +// function testRevokeDefaultGuardian() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// recoverableOpenfortAccount.getGuardians(); + +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); + +// // Verify that the number of guardians is now 2 +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); + +// // Friend account should be a guardian now +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); + +// // Trying to revoke a guardian not using the owner +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + +// // Anyone can confirm a revokation. However, the security period has not passed yet +// skip(1); +// vm.expectRevert(PendingRevokeNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + +// // Anyone can confirm a revokation after security period +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + +// // Default account is not a guardian anymore +// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), false); +// // Verify that the number of guardians is 1 again +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// } + +// /* +// * Test revoking all guardians using owner. +// * Only the owner can revoke a guardian. +// */ +// function testRevokeAllGuardians() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); + +// // Verify that the number of guardians is now 2 +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); + +// // Friend account should be a guardian now +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); + +// // Trying to revoke a guardian not using the owner +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.revokeGuardian(friendAccount); + +// // Trying to revoke a non-existen guardian (random beneficiary address) +// vm.expectRevert(MustBeGuardian.selector); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(beneficiary); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(friendAccount); + +// // Anyone can confirm a revokation. However, the security period has not passed yet +// skip(1); +// vm.expectRevert(PendingRevokeNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); + +// // Anyone can confirm a revokation after security period +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); + +// // Friend account is not a guardian anymore +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// // Verify that the number of guardians is 1 again +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + +// // Anyone can confirm a revokation. However, the security period has not passed yet +// skip(1); +// vm.expectRevert(PendingRevokeNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + +// // Anyone can confirm a revokation after security period +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + +// // Default account is not a guardian anymore +// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), false); +// // Verify that the number of guardians is 1 again +// assertEq(recoverableOpenfortAccount.guardianCount(), 0); +// } + +// /* +// * Test revoking a guardian, but its revocation expired before confirming. +// * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD + SECURITY_WINDOW. +// */ +// function testRevokeEOAGuardianExpired() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(friendAccount); + +// skip(1); +// skip(SECURITY_PERIOD + SECURITY_WINDOW); +// vm.expectRevert(PendingRevokeExpired.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); + +// // Verify that the number of guardians is still 2. No revocation took place +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); + +// // Friend account should still be a guardian +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); +// } + +// /* +// * Test revoking a guardian twice. Make sure a new revocation is not created and the original still works. +// * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD and SECURITY_WINDOW. +// */ +// function testRevokeEOAGuardianDuplicatedPorposal() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); + +// // Verify that the number of guardians is now 2 +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); +// // Friend account should now be a guardian +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) +// recoverableOpenfortAccount.revokeGuardian(friendAccount); + +// vm.expectRevert(DuplicatedRevoke.selector); +// skip(1); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(friendAccount); + +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); + +// // Verify that the number of guardians is now 1 +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // Friend account should not be a guardian anymore +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); +// } + +// /* +// * Test revoking the default guardian and add it back. +// */ +// function testRevokeDefaultGuardianAndAddBack() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) +// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + +// skip(SECURITY_PERIOD + 1); +// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + +// // Verify that the number of guardians is now 0 +// assertEq(recoverableOpenfortAccount.guardianCount(), 0); +// // deault (openfort) account should not be a guardian anymore +// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + +// // Expect that we will see an event containing the deault (openfort) account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + +// skip(SECURITY_PERIOD + 1); +// recoverableOpenfortAccount.confirmGuardianProposal(OPENFORT_GUARDIAN); + +// // Verify that the number of guardians is now 1 again +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); +// // deault (openfort) account should be a guardian again +// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), true); +// } + +// /* +// * Test revoking a guardian using owner and cancel before confirming. +// * Only the owner can revoke a guardian and cancel its revocation before confirming. +// */ +// function testCancelRevokeGuardian() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Verify that the number of guardians is 1 (default) +// assertEq(recoverableOpenfortAccount.guardianCount(), 1); + +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); + +// // Verify that the number of guardians is now 2 +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); +// // Friend account should be a guardian now +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); + +// // Trying to revoke a guardian not using the owner +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.revokeGuardian(friendAccount); + +// // Trying to revoke a non-existen guardian (random beneficiary address) +// vm.expectRevert(MustBeGuardian.selector); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(beneficiary); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(friendAccount); + +// // Anyone can confirm a revokation. However, the security period has not passed yet +// skip(1); +// vm.expectRevert(PendingRevokeNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); + +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.cancelGuardianRevocation(friendAccount); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianRevocationCancelled(friendAccount); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.cancelGuardianRevocation(friendAccount); + +// // Friend account is not a guardian anymore +// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); +// // Verify that the number of guardians is 1 again +// assertEq(recoverableOpenfortAccount.guardianCount(), 2); + +// // Cancelled revocation should not be able to be confirmed now +// skip(SECURITY_PERIOD); +// vm.expectRevert(UnknownRevoke.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); +// } + +// /* +// * Random extra tests to mess up with the logic +// */ +// function testMessingUpWithGuardianRegister() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Create 4 friends +// address friendAccount; +// uint256 friendAccountPK; +// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + +// address friendAccount2; +// uint256 friendAccount2PK; +// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + +// // Adding and removing guardians +// vm.startPrank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); +// recoverableOpenfortAccount.proposeGuardian(friendAccount2); +// vm.stopPrank(); + +// skip(SECURITY_PERIOD + 1); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// vm.expectRevert(MustBeGuardian.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount2); // Notice this tries to confirm a non-existent revocation! +// vm.expectRevert(UnknownRevoke.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); // Notice this tries to confirm a non-existent revocation! +// vm.prank(accountAdmin); +// vm.expectRevert(MustBeGuardian.selector); +// recoverableOpenfortAccount.revokeGuardian(friendAccount2); // Notice this tries to revoke a non-existent guardian! +// vm.expectRevert(DuplicatedGuardian.selector); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); // Notice this tries to register a guardian AGAIN! +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(friendAccount); // Starting a valid revocation process +// skip(SECURITY_PERIOD + 1); +// vm.expectRevert(DuplicatedGuardian.selector); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); // Notice this tries to confirm a guardian that is already valid and pending to revoke! +// } + +// /** +// * Recovery tests * +// */ + +// /* +// * Check the correct functionality of startRecovery() +// */ +// function testStartRecovery() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// vm.expectRevert(MustBeGuardian.selector); +// recoverableOpenfortAccount.startRecovery(OPENFORT_GUARDIAN); + +// vm.prank(OPENFORT_GUARDIAN); +// vm.expectRevert(GuardianCannotBeOwner.selector); +// recoverableOpenfortAccount.startRecovery(OPENFORT_GUARDIAN); + +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); + +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// } + +// /* +// * Checks that incorrect parameters should always fail when trying to complete a recovery +// */ +// function testBasicChecksCompleteRecovery() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); + +// assertEq(recoverableOpenfortAccount.isLocked(), true); + +// // The recovery time period has not passed. The user should wait to recover. +// vm.expectRevert(OngoingRecovery.selector); +// bytes[] memory signatures = new bytes[](1); +// recoverableOpenfortAccount.completeRecovery(signatures); + +// // Providing an empty array when it is expecting one guardian +// skip(RECOVERY_PERIOD + 1); +// vm.expectRevert(InvalidSignatureAmount.selector); +// bytes[] memory signatures_wrong_length = new bytes[](3); +// recoverableOpenfortAccount.completeRecovery(signatures_wrong_length); + +// // Since signatures are empty, it should return an ECDSA error +// vm.expectRevert("ECDSA: invalid signature length"); +// recoverableOpenfortAccount.completeRecovery(signatures); +// } + +// /* +// * Auxiliary function to get a valid EIP712 signature using _eip721contract's domains separator, +// * a valid hash of the message to sign (_structHash) and a private key (_pk) +// */ +// function getEIP712SignatureFrom(address _eip721contract, bytes32 _structHash, uint256 _pk) +// internal +// returns (bytes memory signature721) +// { +// (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = +// IERC5267(_eip721contract).eip712Domain(); +// bytes32 domainSeparator = keccak256( +// abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) +// ); +// bytes32 hash712 = domainSeparator.toTypedDataHash(_structHash); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, hash712); +// signature721 = abi.encodePacked(r, s, v); +// assertEq(ecrecover(hash712, v, r, s), vm.addr(_pk)); +// } + +// /* +// * Most basic, yet complete, recovery flow +// * The default Openfort guardian is used to start and complete a recovery process. +// * Ownership is transferred to beneficiary +// */ +// function testBasicCompleteRecovery() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Default Openfort guardian starts a recovery process because the owner lost the PK +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); +// assertEq(recoverableOpenfortAccount.isLocked(), true); + +// bytes32 structHash = keccak256( +// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) +// ); + +// bytes[] memory signatures = new bytes[](1); +// signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + +// skip(RECOVERY_PERIOD + 1); +// recoverableOpenfortAccount.completeRecovery(signatures); + +// assertEq(recoverableOpenfortAccount.isLocked(), false); +// assertEq(recoverableOpenfortAccount.owner(), address(beneficiary)); +// } + +// /* +// * Case: User added 2 guardians and keeps the default (Openfort) +// * The 2 added guardians (friends) are used to recover the account and transfer +// * the ownership to beneficiary +// * @notice Remember that signatures need to be ordered by the guardian's address. +// */ +// function test3GuardiansCompleteRecovery() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Create two friends +// address friendAccount; +// uint256 friendAccountPK; +// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + +// address friendAccount2; +// uint256 friendAccount2PK; +// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + +// { +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount2); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); +// } + +// { +// // Default Openfort guardian starts a recovery process because the owner lost the PK +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// } + +// bytes32 structHash = keccak256( +// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) +// ); + +// bytes[] memory signatures = new bytes[](2); +// signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address +// signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + +// skip(RECOVERY_PERIOD + 1); +// recoverableOpenfortAccount.completeRecovery(signatures); + +// assertEq(recoverableOpenfortAccount.isLocked(), false); +// assertEq(recoverableOpenfortAccount.owner(), address(beneficiary)); +// } + +// /* +// * Case: User added 2 guardians and keeps the default (Openfort) +// * The 2 added guardians (friends) are used to recover the account and transfer +// * the ownership to beneficiary. Faild due to unsorted signatures +// * @notice Remember that signatures need to be ordered by the guardian's address. +// */ +// function test3GuardiansUnorderedCompleteRecovery() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Create two friends +// address friendAccount; +// uint256 friendAccountPK; +// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + +// address friendAccount2; +// uint256 friendAccount2PK; +// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + +// { +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount2); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); +// } + +// { +// // Default Openfort guardian starts a recovery process because the owner lost the PK +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// } + +// bytes32 structHash = keccak256( +// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) +// ); + +// bytes[] memory signatures = new bytes[](2); +// signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccountPK); // Unsorted! +// signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); + +// skip(RECOVERY_PERIOD + 1); +// vm.expectRevert(InvalidRecoverySignatures.selector); +// recoverableOpenfortAccount.completeRecovery(signatures); + +// // it should still be locked and the admin still be the same +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// assertEq(recoverableOpenfortAccount.owner(), accountAdmin); +// } + +// /* +// * Case: User added 4 guardians and removes the default (Openfort) +// * One guardian (friend) is used to start a recovery process +// * The guardian that initiatied the recovery + another one are used to complete the flow. +// * @notice Remember that signatures need to be ordered by the guardian's address. +// */ +// function test4GuardiansNoDefaultCompleteRecovery() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Create 4 friends +// address friendAccount; +// uint256 friendAccountPK; +// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + +// address friendAccount2; +// uint256 friendAccount2PK; +// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + +// // Create 2 more friends. We don't need their PK now as they are not going to sign +// address friendAccount3 = makeAddr("friend3"); +// address friendAccount4 = makeAddr("friend4"); + +// // Adding and removing guardians +// { +// vm.startPrank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); +// recoverableOpenfortAccount.proposeGuardian(friendAccount2); +// recoverableOpenfortAccount.proposeGuardian(friendAccount3); +// recoverableOpenfortAccount.proposeGuardian(friendAccount4); +// vm.stopPrank(); + +// skip(SECURITY_PERIOD + 1); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount3); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount4); + +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); +// vm.expectRevert(PendingRevokeNotOver.selector); +// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); +// skip(SECURITY_PERIOD + 1); +// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); +// } + +// // Start the recovery process +// { +// // Default Openfort guardian tries starts a recovery process because the owner lost the PK +// // It should not work as it is not a guardian anymore +// vm.expectRevert(MustBeGuardian.selector); +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); +// vm.prank(friendAccount2); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// } + +// bytes32 structHash = keccak256( +// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) +// ); + +// bytes[] memory signatures = new bytes[](2); +// signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address +// signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + +// skip(RECOVERY_PERIOD + 1); +// recoverableOpenfortAccount.completeRecovery(signatures); + +// assertEq(recoverableOpenfortAccount.isLocked(), false); +// assertEq(recoverableOpenfortAccount.owner(), address(beneficiary)); +// } + +// /* +// * Case: User added 2 guardians and keeps the default (Openfort) +// * The 2 added guardians (friends) are used to recover the account and transfer +// * the ownership to beneficiary. Wrong signatures used +// * @notice Remember that signatures need to be ordered by the guardian's address. +// */ +// function test3GuardiansFailCompleteRecovery() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Create two friends +// address friendAccount; +// uint256 friendAccountPK; +// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + +// address friendAccount2; +// uint256 friendAccount2PK; +// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + +// { +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount2); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); +// } + +// { +// // Default Openfort guardian starts a recovery process because the owner lost the PK +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// } + +// // notice: wrong new oner!!! +// bytes32 structHash = +// keccak256(abi.encode(RECOVER_TYPEHASH, factoryAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); + +// bytes[] memory signatures = new bytes[](2); +// signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address +// signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + +// skip(RECOVERY_PERIOD + 1); +// vm.expectRevert(InvalidRecoverySignatures.selector); +// recoverableOpenfortAccount.completeRecovery(signatures); + +// // it should still be locked and the admin still be the same +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// assertEq(recoverableOpenfortAccount.owner(), accountAdmin); +// } + +// /* +// * Testing the functionality to cancel a recovery process +// */ +// function testCancelRecovery() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Default Openfort guardian starts a recovery process because the owner lost the PK +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); +// assertEq(recoverableOpenfortAccount.isLocked(), true); + +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.cancelRecovery(); + +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.cancelRecovery(); + +// bytes32 structHash = keccak256( +// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) +// ); + +// bytes[] memory signatures = new bytes[](1); +// signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + +// skip(RECOVERY_PERIOD + 1); +// vm.expectRevert(NoOngoingRecovery.selector); +// recoverableOpenfortAccount.completeRecovery(signatures); + +// assertEq(recoverableOpenfortAccount.isLocked(), false); +// assertEq(recoverableOpenfortAccount.owner(), accountAdmin); +// } + +// /* +// * Testing the startRecovery twice in a row +// */ +// function testStartRecoveryTwice() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Default Openfort guardian starts a recovery process because the owner lost the PK +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); +// assertEq(recoverableOpenfortAccount.isLocked(), true); + +// // Calling startRecovery() again should revert and have no effect +// vm.expectRevert(OngoingRecovery.selector); +// vm.prank(OPENFORT_GUARDIAN); +// recoverableOpenfortAccount.startRecovery(address(beneficiary)); + +// // The accounts should still be locked +// assertEq(recoverableOpenfortAccount.isLocked(), true); +// assertEq(recoverableOpenfortAccount.owner(), accountAdmin); +// } + +// /** +// * Transfer ownership tests * +// */ + +// /* +// * Try to transfer ownership to a guardian. +// * Should not be allowed. +// */ +// function testTransferOwnerNotGuardian() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Create a friend EOA +// address friendAccount = makeAddr("friend"); + +// // Expect that we will see an event containing the friend account and security period +// vm.expectEmit(true, true, false, true); +// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.proposeGuardian(friendAccount); + +// skip(1); +// skip(SECURITY_PERIOD); +// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); + +// // It should fail as friendAccount is a guardian +// vm.expectRevert(GuardianCannotBeOwner.selector); +// recoverableOpenfortAccount.transferOwnership(friendAccount); +// } + +// /* +// * Try to transfer ownership to a valid account. +// * Should be allowed. +// */ +// function testTransferOwner() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); + +// // Create a new owner EOA +// address newOwner = makeAddr("newOwner"); + +// vm.expectRevert("Ownable: caller is not the owner"); +// recoverableOpenfortAccount.transferOwnership(newOwner); + +// vm.prank(accountAdmin); +// recoverableOpenfortAccount.transferOwnership(newOwner); + +// vm.prank(newOwner); +// recoverableOpenfortAccount.acceptOwnership(); + +// // New owner should be now newOwner +// assertEq(recoverableOpenfortAccount.owner(), address(newOwner)); +// } + +// /* +// * Temporal test function for coverage purposes showing +// * that isGuardianOrGuardianSigner() always returns false. +// */ +// function testStubFakeMockTempisGuardian() public { +// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); +// assertEq(recoverableOpenfortAccount.isGuardianOrGuardianSigner(OPENFORT_GUARDIAN), false); +// } +// } From 043ae2d5f93bc838da874567d93b41b96889f028 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 20 Nov 2023 17:44:45 +0100 Subject: [PATCH 26/83] All tests working after big refactor --- .../core/base/BaseRecoverableAccount.sol | 7 - .../core/managed/ManagedOpenfortFactory.sol | 9 +- .../UpgradeableOpenfortAccount.sol | 15 +- script/deployManagedAccounts.sol | 88 +- script/deployUpgradeableAccounts.sol | 94 +- test/foundry/core/OpenfortBaseTest.t.sol | 153 + .../managed/ManagedOpenfortAccountTest.t.sol | 1604 +++++++- .../RecoverableOpenfortAccountTest.t.sol | 2363 ------------ .../UpgradeableOpenfortAccountTest.t.sol | 1422 ++++++- .../paymaster/OpenfortPaymasterV2Test.t.sol | 3360 ++++++++--------- 10 files changed, 4684 insertions(+), 4431 deletions(-) create mode 100644 test/foundry/core/OpenfortBaseTest.t.sol delete mode 100644 test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index fa1ef2d..bab0ca2 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -120,13 +120,6 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg emit GuardianAdded(_openfortGuardian); } - /** - * Return the current EntryPoint - */ - function entryPoint() public view override returns (IEntryPoint) { - return IEntryPoint(entrypointContract); - } - function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { return OwnableUpgradeable.owner(); } diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index e1ae1e2..4fddbeb 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -71,17 +71,10 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, UpgradeableBeacon { function getAddressWithNonce(address _admin, bytes32 _nonce) public view returns (address) { bytes32 salt = keccak256(abi.encode(_admin, _nonce)); return Create2.computeAddress( - salt, - keccak256( - abi.encodePacked( - type(OpenfortManagedProxy).creationCode, - abi.encode(address(this), "") - ) - ) + salt, keccak256(abi.encodePacked(type(OpenfortManagedProxy).creationCode, abi.encode(address(this), ""))) ); } - /** * @dev {See BaseOpenfortFactory} */ diff --git a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol index 562bc7a..b9d07b8 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol @@ -17,12 +17,17 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U * - UUPSUpgradeable */ contract UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { - event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); - function _authorizeUpgrade(address) internal override onlyOwner {} - function owner() public view virtual override(BaseRecoverableAccount, OwnableUpgradeable) returns (address) { - return OwnableUpgradeable.owner(); + function owner() public view virtual override returns (address) { + return super.owner(); + } + + /** + * Return the current EntryPoint + */ + function entryPoint() public view override returns (IEntryPoint) { + return IEntryPoint(entrypointContract); } /** @@ -35,6 +40,4 @@ contract UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { emit EntryPointUpdated(entrypointContract, _newEntrypoint); entrypointContract = _newEntrypoint; } - - function _authorizeUpgrade(address) internal override onlyOwner {} } diff --git a/script/deployManagedAccounts.sol b/script/deployManagedAccounts.sol index 813ed2b..a3e1ce8 100644 --- a/script/deployManagedAccounts.sol +++ b/script/deployManagedAccounts.sol @@ -7,47 +7,47 @@ import {ManagedOpenfortAccount} from "../contracts/core/managed/ManagedOpenfortA import {ManagedOpenfortFactory} from "../contracts/core/managed/ManagedOpenfortFactory.sol"; // import {MockedV2ManagedOpenfortAccount} from "../contracts/mock/MockedV2ManagedOpenfortAccount.sol"; -contract ManagedOpenfortDeploy is Script { - uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); - address internal deployAddress = vm.addr(deployPrivKey); - IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - - function run() public { - bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); - vm.startBroadcast(deployPrivKey); - - // Create an acccount to server as implementation - ManagedOpenfortAccount managedOpenfortAccount = new ManagedOpenfortAccount{salt: versionSalt}(); - - // OpenfortBeacon openfortBeacon = new OpenfortBeacon(address(managedOpenfortAccount)); // not needed anymore - - // Create a factory to deploy cloned accounts - ManagedOpenfortFactory managedOpenfortFactory = - new ManagedOpenfortFactory{salt: versionSalt}(deployAddress, address(entryPoint), address(managedOpenfortAccount)); - (managedOpenfortFactory); - // address account1 = managedOpenfortFactory.accountImplementation(); - - // The first call should create a new account, while the second will just return the corresponding account address - // address account2 = managedOpenfortFactory.createAccountWithNonce(deployAddress, "1"); - // console.log( - // "Factory at address %s has created an account at address %s", address(managedOpenfortFactory), account2 - // ); - - // MockedV2ManagedOpenfortAccount mockedOpenfortAccount = new MockedV2ManagedOpenfortAccount{salt: versionSalt}(); - // (mockedOpenfortAccount); - - // assert(account1 != account2); - // address account3 = managedOpenfortFactory.createAccountWithNonce(deployAddress, 3); - // console.log( - // "Factory at address %s has created an account at address %s", address(managedOpenfortFactory), account3 - // ); - // assert(account2 != account3); - // address account4 = managedOpenfortFactory.createAccountWithNonce(deployAddress, 4); - // console.log( - // "Factory at address %s has created an account at address %s", address(managedOpenfortFactory), account4 - // ); - // assert(account3 != account4); - - vm.stopBroadcast(); - } -} +// contract ManagedOpenfortDeploy is Script { +// uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); +// address internal deployAddress = vm.addr(deployPrivKey); +// IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); + +// function run() public { +// bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); +// vm.startBroadcast(deployPrivKey); + +// // Create an acccount to server as implementation +// ManagedOpenfortAccount managedOpenfortAccount = new ManagedOpenfortAccount{salt: versionSalt}(); + +// // OpenfortBeacon openfortBeacon = new OpenfortBeacon(address(managedOpenfortAccount)); // not needed anymore + +// // Create a factory to deploy cloned accounts +// ManagedOpenfortFactory managedOpenfortFactory = +// new ManagedOpenfortFactory{salt: versionSalt}(deployAddress, address(entryPoint), address(managedOpenfortAccount)); +// (managedOpenfortFactory); +// // address account1 = managedOpenfortFactory.accountImplementation(); + +// // The first call should create a new account, while the second will just return the corresponding account address +// // address account2 = managedOpenfortFactory.createAccountWithNonce(deployAddress, "1"); +// // console.log( +// // "Factory at address %s has created an account at address %s", address(managedOpenfortFactory), account2 +// // ); + +// // MockedV2ManagedOpenfortAccount mockedOpenfortAccount = new MockedV2ManagedOpenfortAccount{salt: versionSalt}(); +// // (mockedOpenfortAccount); + +// // assert(account1 != account2); +// // address account3 = managedOpenfortFactory.createAccountWithNonce(deployAddress, 3); +// // console.log( +// // "Factory at address %s has created an account at address %s", address(managedOpenfortFactory), account3 +// // ); +// // assert(account2 != account3); +// // address account4 = managedOpenfortFactory.createAccountWithNonce(deployAddress, 4); +// // console.log( +// // "Factory at address %s has created an account at address %s", address(managedOpenfortFactory), account4 +// // ); +// // assert(account3 != account4); + +// vm.stopBroadcast(); +// } +// } diff --git a/script/deployUpgradeableAccounts.sol b/script/deployUpgradeableAccounts.sol index 88c9e08..10c6db0 100644 --- a/script/deployUpgradeableAccounts.sol +++ b/script/deployUpgradeableAccounts.sol @@ -6,50 +6,50 @@ import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPo import {UpgradeableOpenfortAccount} from "../contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; -contract UpgradeableOpenfortDeploy is Script { - uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC_MC"), 0); - // uint256 internal deployPrivKey = vm.envUint("PK"); - address internal deployAddress = vm.addr(deployPrivKey); - IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - - function run() public { - bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); - vm.startBroadcast(deployPrivKey); - - UpgradeableOpenfortAccount upgradeableOpenfortAccount = new UpgradeableOpenfortAccount{salt: versionSalt}(); - - UpgradeableOpenfortFactory upgradeableOpenfortFactory = - new UpgradeableOpenfortFactory{salt: versionSalt}(address(entryPoint), address(upgradeableOpenfortAccount)); - (upgradeableOpenfortFactory); - // address account1 = upgradeableOpenfortFactory.accountImplementation(); - - // The first call should create a new account, while the second will just return the corresponding account address - address account2 = upgradeableOpenfortFactory.createAccountWithNonce(deployAddress, "1"); - console.log( - "Factory at address %s has created an account at address %s", address(upgradeableOpenfortFactory), account2 - ); - - // assert(account1 != account2); - // address account3 = upgradeableOpenfortFactory.createAccountWithNonce(deployAddress, 3); - // console.log( - // "Factory at address %s has created an account at address %s", address(upgradeableOpenfortFactory), account3 - // ); - // assert(account2 != account3); - // address account4 = upgradeableOpenfortFactory.createAccountWithNonce(deployAddress, 4); - // console.log( - // "Factory at address %s has created an account at address %s", address(upgradeableOpenfortFactory), account4 - // ); - // assert(account3 != account4); - - //address account3 = upgradeableOpenfortFactory.createAccount(deployAddress, bytes("")); - - //assert(account1 != account2); - //assert(account2 == account3); - - //UpgradeableOpenfortAccount newAccount = new UpgradeableOpenfortAccount(); - - //UpgradeableOpenfortAccount(payable(account2)).upgradeTo(address(newAccount)); - - vm.stopBroadcast(); - } -} +// contract UpgradeableOpenfortDeploy is Script { +// uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC_MC"), 0); +// // uint256 internal deployPrivKey = vm.envUint("PK"); +// address internal deployAddress = vm.addr(deployPrivKey); +// IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); + +// function run() public { +// bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); +// vm.startBroadcast(deployPrivKey); + +// UpgradeableOpenfortAccount upgradeableOpenfortAccount = new UpgradeableOpenfortAccount{salt: versionSalt}(); + +// UpgradeableOpenfortFactory upgradeableOpenfortFactory = +// new UpgradeableOpenfortFactory{salt: versionSalt}(address(entryPoint), address(upgradeableOpenfortAccount)); +// (upgradeableOpenfortFactory); +// // address account1 = upgradeableOpenfortFactory.accountImplementation(); + +// // The first call should create a new account, while the second will just return the corresponding account address +// address account2 = upgradeableOpenfortFactory.createAccountWithNonce(deployAddress, "1"); +// console.log( +// "Factory at address %s has created an account at address %s", address(upgradeableOpenfortFactory), account2 +// ); + +// // assert(account1 != account2); +// // address account3 = upgradeableOpenfortFactory.createAccountWithNonce(deployAddress, 3); +// // console.log( +// // "Factory at address %s has created an account at address %s", address(upgradeableOpenfortFactory), account3 +// // ); +// // assert(account2 != account3); +// // address account4 = upgradeableOpenfortFactory.createAccountWithNonce(deployAddress, 4); +// // console.log( +// // "Factory at address %s has created an account at address %s", address(upgradeableOpenfortFactory), account4 +// // ); +// // assert(account3 != account4); + +// //address account3 = upgradeableOpenfortFactory.createAccount(deployAddress, bytes("")); + +// //assert(account1 != account2); +// //assert(account2 == account3); + +// //UpgradeableOpenfortAccount newAccount = new UpgradeableOpenfortAccount(); + +// //UpgradeableOpenfortAccount(payable(account2)).upgradeTo(address(newAccount)); + +// vm.stopBroadcast(); +// } +// } diff --git a/test/foundry/core/OpenfortBaseTest.t.sol b/test/foundry/core/OpenfortBaseTest.t.sol new file mode 100644 index 0000000..544bbc1 --- /dev/null +++ b/test/foundry/core/OpenfortBaseTest.t.sol @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {Test, console} from "lib/forge-std/src/Test.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; +import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +import {TestCounter} from "account-abstraction/test/TestCounter.sol"; +import {MockERC20} from "contracts/mock/MockERC20.sol"; +import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; + +contract OpenfortBaseTest is Test { + using ECDSA for bytes32; + + bytes32 public versionSalt; + + EntryPoint public entryPoint; + + address public account; + TestCounter public testCounter; + MockERC20 public mockERC20; + + // Testing addresses + address public factoryAdmin; + uint256 public factoryAdminPKey; + + address public accountAdmin; + uint256 public accountAdminPKey; + + address payable public beneficiary = payable(makeAddr("beneficiary")); + + // bytes4(keccak256("isValidSignature(bytes32,bytes)") + bytes4 public constant MAGICVALUE = 0x1626ba7e; + // keccak256("OpenfortMessage(bytes32 hashedMessage)"); + bytes32 public constant OF_MSG_TYPEHASH = 0x57159f03b9efda178eab2037b2ec0b51ce11be0051b8a2a9992c29dc260e4a30; + bytes32 public constant _TYPE_HASH = + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + + // keccak256("Recover(address recoveryAddress,uint64 executeAfter,uint32 guardiansRequired)"); + bytes32 public RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; + + uint256 public constant RECOVERY_PERIOD = 2 days; + uint256 public constant SECURITY_PERIOD = 1.5 days; + uint256 public constant SECURITY_WINDOW = 0.5 days; + uint256 public constant LOCK_PERIOD = 5 days; + address public OPENFORT_GUARDIAN; + uint256 public OPENFORT_GUARDIAN_PKEY; + + event AccountImplementationDeployed(address indexed creator); + event AccountCreated(address indexed account, address indexed accountAdmin); + event GuardianProposed(address indexed guardian, uint256 executeAfter); + event GuardianProposalCancelled(address indexed guardian); + event GuardianRevocationRequested(address indexed guardian, uint256 executeAfter); + event GuardianRevocationCancelled(address indexed guardian); + + error ZeroAddressNotAllowed(); + error AccountLocked(); + error AccountNotLocked(); + error MustBeGuardian(); + error DuplicatedGuardian(); + error UnknownProposal(); + error PendingProposalNotOver(); + error PendingProposalExpired(); + error DuplicatedRevoke(); + error UnknownRevoke(); + error PendingRevokeNotOver(); + error PendingRevokeExpired(); + error GuardianCannotBeOwner(); + error NoOngoingRecovery(); + error OngoingRecovery(); + error InvalidRecoverySignatures(); + error InvalidSignatureAmount(); + + /* + * Auxiliary function to generate a userOP + */ + function _setupUserOp( + address sender, + uint256 _signerPKey, + bytes memory _initCode, + bytes memory _callDataForEntrypoint + ) public returns (UserOperation[] memory ops) { + uint256 nonce = entryPoint.getNonce(sender, 0); + + // Get user op fields + UserOperation memory op = UserOperation({ + sender: sender, + nonce: nonce, + initCode: _initCode, + callData: _callDataForEntrypoint, + callGasLimit: 1_000_000, + verificationGasLimit: 1_000_000, + preVerificationGas: 50_000, + maxFeePerGas: 0, + maxPriorityFeePerGas: 0, + paymasterAndData: bytes(""), + signature: bytes("") + }); + + // Sign UserOp + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); + bytes memory userOpSignature = abi.encodePacked(r, s, v); + + address recoveredSigner = ECDSA.recover(msgHash, v, r, s); + address expectedSigner = vm.addr(_signerPKey); + assertEq(recoveredSigner, expectedSigner); + + op.signature = userOpSignature; + + // Store UserOp + ops = new UserOperation[](1); + ops[0] = op; + } + + /* + * Auxiliary function to generate a userOP using the execute() + * from the account + */ + function _setupUserOpExecute( + address sender, + uint256 _signerPKey, + bytes memory _initCode, + address _target, + uint256 _value, + bytes memory _callData + ) public returns (UserOperation[] memory) { + bytes memory callDataForEntrypoint = + abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); + + return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); + } + + /* + * Auxiliary function to generate a userOP using the executeBatch() + * from the account + */ + function _setupUserOpExecuteBatch( + address sender, + uint256 _signerPKey, + bytes memory _initCode, + address[] memory _target, + uint256[] memory _value, + bytes[] memory _callData + ) public returns (UserOperation[] memory) { + bytes memory callDataForEntrypoint = + abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); + + return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); + } +} diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index e237259..ba11a69 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Test, console} from "lib/forge-std/src/Test.sol"; +import {console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC20} from "contracts/mock/MockERC20.sol"; @@ -10,108 +11,13 @@ import {ManagedOpenfortAccount} from "contracts/core/managed/ManagedOpenfortAcco import {ManagedOpenfortFactory} from "contracts/core/managed/ManagedOpenfortFactory.sol"; import {OpenfortManagedProxy} from "contracts/core/managed/OpenfortManagedProxy.sol"; import {MockV2ManagedOpenfortAccount} from "contracts/mock/MockV2ManagedOpenfortAccount.sol"; +import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; -contract ManagedOpenfortAccountTest is Test { +contract ManagedOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; - EntryPoint public entryPoint; - // OpenfortBeacon public openfortBeacon; // not needed anymore ManagedOpenfortAccount public managedOpenfortAccount; ManagedOpenfortFactory public managedOpenfortFactory; - address public account; - TestCounter public testCounter; - MockERC20 public mockERC20; - - // Testing addresses - address private factoryAdmin; - uint256 private factoryAdminPKey; - - address private accountAdmin; - uint256 private accountAdminPKey; - - address payable private beneficiary = payable(makeAddr("beneficiary")); - - event AccountCreated(address indexed account, address indexed accountAdmin); - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (UserOperation[] memory ops) { - uint256 nonce = entryPoint.getNonce(sender, 0); - - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 0, - maxPriorityFeePerGas: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } /** * @notice Initialize the ManagedOpenfortAccount testing contract. @@ -127,6 +33,7 @@ contract ManagedOpenfortAccountTest is Test { vm.deal(factoryAdmin, 100 ether); (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); vm.deal(accountAdmin, 100 ether); + (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); vm.startPrank(factoryAdmin); // If we are in a fork @@ -142,12 +49,18 @@ contract ManagedOpenfortAccountTest is Test { entryPoint = EntryPoint(payable(targetAddr)); } // deploy account implementation - managedOpenfortAccount = new ManagedOpenfortAccount(); - // deploy OpenfortBeacon - // openfortBeacon = new OpenfortBeacon(address(managedOpenfortAccount)); // not needed anymore + managedOpenfortAccount = new ManagedOpenfortAccount{salt: versionSalt}(); // deploy account factory (beacon) - managedOpenfortFactory = - new ManagedOpenfortFactory(factoryAdmin, address(entryPoint), address(managedOpenfortAccount)); + managedOpenfortFactory = new ManagedOpenfortFactory{salt: versionSalt}( + factoryAdmin, + address(entryPoint), + address(managedOpenfortAccount), + RECOVERY_PERIOD, + SECURITY_PERIOD, + SECURITY_WINDOW, + LOCK_PERIOD, + OPENFORT_GUARDIAN + ); // Create an static account wallet and get its address account = managedOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); // deploy a new TestCounter @@ -976,86 +889,86 @@ contract ManagedOpenfortAccountTest is Test { /* * Basic test of simulateValidation() to check that it always reverts. */ - function testSimulateValidation() public { - // Verify that the counter is stil set to 0 - assertEq(testCounter.counters(account), 0); + // function testSimulateValidation() public { + // // Verify that the counter is stil set to 0 + // assertEq(testCounter.counters(account), 0); - UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); + // UserOperation[] memory userOp = _setupUserOpExecute( + // account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + // ); - entryPoint.depositTo{value: 1000000000000000000}(account); + // entryPoint.depositTo{value: 1000000000000000000}(account); - // Expect the simulateValidation() to always revert - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); + // // Expect the simulateValidation() to always revert + // vm.expectRevert(); + // entryPoint.simulateValidation(userOp[0]); - // Test addStake. Make sure it checks for owner and alue passed. - vm.expectRevert("Ownable: caller is not the owner"); - managedOpenfortFactory.addStake{value: 10000000000000000}(99); - vm.prank(factoryAdmin); - vm.expectRevert("no stake specified"); - managedOpenfortFactory.addStake(99); - vm.prank(factoryAdmin); - managedOpenfortFactory.addStake{value: 10000000000000000}(99); + // // Test addStake. Make sure it checks for owner and alue passed. + // vm.expectRevert("Ownable: caller is not the owner"); + // managedOpenfortFactory.addStake{value: 10000000000000000}(99); + // vm.prank(factoryAdmin); + // vm.expectRevert("no stake specified"); + // managedOpenfortFactory.addStake(99); + // vm.prank(factoryAdmin); + // managedOpenfortFactory.addStake{value: 10000000000000000}(99); - // expectRevert as simulateValidation() always reverts - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); + // // expectRevert as simulateValidation() always reverts + // vm.expectRevert(); + // entryPoint.simulateValidation(userOp[0]); - // expectRevert as simulateHandleOp() always reverts - vm.expectRevert(); - entryPoint.simulateHandleOp(userOp[0], address(0), ""); + // // expectRevert as simulateHandleOp() always reverts + // vm.expectRevert(); + // entryPoint.simulateHandleOp(userOp[0], address(0), ""); - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } + // // Verify that the counter has not increased + // assertEq(testCounter.counters(account), 0); + // } - /* - * 1- Deploy a factory using the old EntryPoint to create an account. - * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() - */ - function testUpgradeTo() public { - // Create a managed account wallet using the old implementation and get its address - vm.prank(factoryAdmin); - address payable accountOld = payable(managedOpenfortFactory.createAccountWithNonce(accountAdmin, "2")); - ManagedOpenfortAccount managedAccount = ManagedOpenfortAccount(accountOld); - assertEq(managedAccount.owner(), accountAdmin); - assertEq(address(managedAccount.entryPoint()), address(entryPoint)); + // /* + // * 1- Deploy a factory using the old EntryPoint to create an account. + // * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() + // */ + // function testUpgradeTo() public { + // // Create a managed account wallet using the old implementation and get its address + // vm.prank(factoryAdmin); + // address payable accountOld = payable(managedOpenfortFactory.createAccountWithNonce(accountAdmin, "2")); + // ManagedOpenfortAccount managedAccount = ManagedOpenfortAccount(accountOld); + // assertEq(managedAccount.owner(), accountAdmin); + // assertEq(address(managedAccount.entryPoint()), address(entryPoint)); - OpenfortManagedProxy p = OpenfortManagedProxy(payable(account)); - // Printing account address and the implementation address - console.log(account); - console.log(p.implementation()); + // OpenfortManagedProxy p = OpenfortManagedProxy(payable(account)); + // // Printing account address and the implementation address + // console.log(account); + // console.log(p.implementation()); - // Deploy the new implementation - MockV2ManagedOpenfortAccount newImplementation = new MockV2ManagedOpenfortAccount(); - address newImplementationAddress = address(newImplementation); + // // Deploy the new implementation + // MockV2ManagedOpenfortAccount newImplementation = new MockV2ManagedOpenfortAccount(); + // address newImplementationAddress = address(newImplementation); - vm.expectRevert("Ownable: caller is not the owner"); - managedOpenfortFactory.upgradeTo(newImplementationAddress); + // vm.expectRevert("Ownable: caller is not the owner"); + // managedOpenfortFactory.upgradeTo(newImplementationAddress); - vm.prank(factoryAdmin); - managedOpenfortFactory.upgradeTo(newImplementationAddress); + // vm.prank(factoryAdmin); + // managedOpenfortFactory.upgradeTo(newImplementationAddress); - assertEq(managedOpenfortFactory.accountImplementation(), newImplementationAddress); - //redundant view call for now (due to factory being the Beacon now) - assertEq(managedOpenfortFactory.implementation(), newImplementationAddress); + // assertEq(managedOpenfortFactory.accountImplementation(), newImplementationAddress); + // //redundant view call for now (due to factory being the Beacon now) + // assertEq(managedOpenfortFactory.implementation(), newImplementationAddress); - // Notice that, even though we bind the address to the old implementation, entryPoint() is now 0 - assertEq(address(managedAccount.entryPoint()), address(0)); + // // Notice that, even though we bind the address to the old implementation, entryPoint() is now 0 + // assertEq(address(managedAccount.entryPoint()), address(0)); - // Same for new accounts. From now on, they have the new owner(). - vm.prank(factoryAdmin); - address payable account3 = payable(managedOpenfortFactory.createAccountWithNonce(accountAdmin, "3")); - ManagedOpenfortAccount managedAccount3 = ManagedOpenfortAccount(account3); + // // Same for new accounts. From now on, they have the new owner(). + // vm.prank(factoryAdmin); + // address payable account3 = payable(managedOpenfortFactory.createAccountWithNonce(accountAdmin, "3")); + // ManagedOpenfortAccount managedAccount3 = ManagedOpenfortAccount(account3); - assertEq(address(managedAccount3.entryPoint()), address(0)); + // assertEq(address(managedAccount3.entryPoint()), address(0)); - // Printing account address and the implementation address. Impl address should have changed - console.log(account); - console.log(p.implementation()); - } + // // Printing account address and the implementation address. Impl address should have changed + // console.log(account); + // console.log(p.implementation()); + // } /* * 1- Deploy a factory using the old EntryPoint to create an account. @@ -1082,4 +995,1363 @@ contract ManagedOpenfortAccountTest is Test { // assertEq(address(managedAccount.entryPoint()), newEntryPoint); // } + + function testFailIsValidSignature() public { + bytes32 hash = keccak256("Signed by Owner"); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hash); + address signer = ecrecover(hash, v, r, s); + assertEq(accountAdmin, signer); // [PASS] + + bytes memory signature = abi.encodePacked(r, s, v); + signer = ECDSA.recover(hash, signature); + assertEq(accountAdmin, signer); // [PASS] + + bytes4 valid = ManagedOpenfortAccount(payable(account)).isValidSignature(hash, signature); + assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! + assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore + } + + function testFailIsValidSignatureMessage() public { + bytes32 hash = keccak256("Signed by Owner"); + bytes32 hashMessage = hash.toEthSignedMessageHash(); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hashMessage); + address signer = ecrecover(hashMessage, v, r, s); + assertEq(accountAdmin, signer); // [PASS] + + bytes memory signature = abi.encodePacked(r, s, v); + signer = ECDSA.recover(hashMessage, signature); + assertEq(accountAdmin, signer); // [PASS] + + bytes4 valid = ManagedOpenfortAccount(payable(account)).isValidSignature(hash, signature); + assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! + assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore + } + + /* + * Auxiliary function to get a valid EIP712 signature using _eip721contract's domains separator, + * a valid hash of the message to sign (_structHash) and a private key (_pk) + */ + function getEIP712SignatureFrom(address _eip721contract, bytes32 _structHash, uint256 _pk) + internal + returns (bytes memory signature721) + { + (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = + IERC5267(_eip721contract).eip712Domain(); + bytes32 domainSeparator = keccak256( + abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) + ); + bytes32 hash712 = domainSeparator.toTypedDataHash(_structHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, hash712); + signature721 = abi.encodePacked(r, s, v); + assertEq(ecrecover(hash712, v, r, s), vm.addr(_pk)); + } + + function testisValidSignatureTyped() public { + string memory messageToSign = "Signed by Owner"; + bytes32 hash = keccak256(abi.encodePacked(messageToSign)); + + bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, hash)); + + (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = + IERC5267(account).eip712Domain(); + + bytes32 domainSeparator = keccak256( + abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) + ); + + bytes memory signature = getEIP712SignatureFrom(account, structHash, accountAdminPKey); + bytes32 hash712 = domainSeparator.toTypedDataHash(structHash); + address signer = hash712.recover(signature); + + assertEq(accountAdmin, signer); // [PASS] + + bytes4 valid = ManagedOpenfortAccount(payable(account)).isValidSignature(hash, signature); + assertEq(valid, MAGICVALUE); // SHOULD PASS + } + + /** + * Lock tests * + */ + + /* + * Test locking the Openfort account using the default guardian. + */ + function testLockAccount() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.getLock(), 0); + + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.lock(); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.lock(); + + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.getLock(), block.timestamp + LOCK_PERIOD); + + vm.expectRevert(AccountLocked.selector); + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.lock(); + + // Automatically unlock + skip(LOCK_PERIOD + 1); + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.getLock(), 0); + } + + /* + * Test unlocking the Openfort account using the default guardian. + */ + function testUnlockAccount() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.getLock(), 0); + + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.lock(); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.lock(); + + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.getLock(), block.timestamp + LOCK_PERIOD); + + skip(LOCK_PERIOD / 2); + + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.unlock(); + assertEq(openfortAccount.isLocked(), true); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.unlock(); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.getLock(), 0); + + vm.expectRevert(AccountNotLocked.selector); + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.unlock(); + } + + /** + * Add guardians tests * + */ + + /* + * Test proposing a guardian (by the owner) and accepting it (by the owner). + * Successfully propose a guardian and confirm it after SECURITY_PERIOD + */ + function testAddEOAGuardian() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + openfortAccount.getGuardians(); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Trying to proposa a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.proposeGuardian(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + // Test if zero address is a guardian + assertEq(openfortAccount.isGuardian(address(0)), false); + + skip(1); + + vm.expectRevert(PendingProposalNotOver.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + } + + /* + * Test proposing a guardian, but its proposal expires before accepting. + * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. + */ + function testAddEOAGuardianExpired() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Trying to proposa a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.proposeGuardian(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + + vm.expectRevert(PendingProposalNotOver.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + skip(SECURITY_PERIOD + SECURITY_WINDOW); + vm.expectRevert(PendingProposalExpired.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + } + + /* + * Test proposing a guardian, but its proposal expires before accepting. Re-add again + * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. + */ + function testAddEOAGuardianExpiredThenReAdd() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Trying to proposa a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.proposeGuardian(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + + vm.expectRevert(PendingProposalNotOver.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + skip(SECURITY_PERIOD + SECURITY_WINDOW); + vm.expectRevert(PendingProposalExpired.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + /* Let's try it again (re-add) */ + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), true); + } + + /* + * Test proposing a guardian twice. Make sure a new proposal is not created and the original still works. + * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. + */ + function testAddEOAGuardianDuplicatedPorposal() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + + vm.expectRevert(); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), true); + } + + /* + * Test proposing a guardian and cancel its proposal before accepting or expiring + * Only the owner can cancel an ongoing proposal. + */ + function testAddEOAGuardianCancel() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Trying to proposa a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.proposeGuardian(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + vm.expectRevert(PendingProposalNotOver.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + skip(SECURITY_PERIOD); + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.cancelGuardianProposal(friendAccount); + + vm.expectEmit(true, true, false, true); + emit GuardianProposalCancelled(friendAccount); + vm.prank(accountAdmin); + openfortAccount.cancelGuardianProposal(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + vm.prank(accountAdmin); + vm.expectRevert(UnknownProposal.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + } + + /* + * Test proposing owner as guardian. It should revert. + * Successfully propose a guardian and confirm it after SECURITY_PERIOD + */ + function testAddOwnerAsGuardianNotAllowed() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + openfortAccount.getGuardians(); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Expect revert because the owner cannot be proposed as guardian + vm.expectRevert(); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(accountAdmin); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Owner account should not be a guardian yet + assertEq(openfortAccount.isGuardian(accountAdmin), false); + + // Expect revert because the default guardian cannot be proposed again + vm.expectRevert(DuplicatedGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // OPENFORT_GUARDIAN account should stil be a guardian + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); + } + + /* + * Test proposing multiple guardians (by the owner) and accepting them afterwards (by the owner). + * Successfully propose guardians and confirm them after SECURITY_PERIOD + */ + function testAddMultipleEOAGuardians() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + openfortAccount.getGuardians(); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create multiple friend EOAs + address[] memory friends = new address[](5); + friends[0] = makeAddr("friend"); + friends[1] = makeAddr("friend2"); + friends[2] = makeAddr("friend3"); + friends[3] = makeAddr("friend4"); + friends[4] = makeAddr("friend5"); + + for (uint256 index = 0; index < friends.length; index++) { + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friends[index], block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friends[index]); + } + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friends[0]), false); + + skip(1); + skip(SECURITY_PERIOD); + + for (uint256 index = 0; index < friends.length; index++) { + openfortAccount.confirmGuardianProposal(friends[index]); + } + + // Verify that the number of guardians is now 6 + assertEq(openfortAccount.guardianCount(), 6); + + // First friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friends[0]), true); + } + + /** + * Revoke guardians tests * + */ + + /* + * Test revoking a guardian using owner. + * Only the owner can revoke a guardian. + */ + function testRevokeGuardian() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Trying to revoke a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.revokeGuardian(friendAccount); + + // Trying to revoke a non-existen guardian (random beneficiary address) + vm.expectRevert(MustBeGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(beneficiary); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Anyone can confirm a revokation after security period + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Friend account is not a guardian anymore + assertEq(openfortAccount.isGuardian(friendAccount), false); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 1); + } + + /* + * Test revoking the default guardian when having registered another (custom) one. + * Only the owner can revoke a guardian. + */ + function testRevokeDefaultGuardian() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + openfortAccount.getGuardians(); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Trying to revoke a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Anyone can confirm a revokation after security period + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Default account is not a guardian anymore + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 1); + } + + /* + * Test revoking all guardians using owner. + * Only the owner can revoke a guardian. + */ + function testRevokeAllGuardians() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Trying to revoke a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.revokeGuardian(friendAccount); + + // Trying to revoke a non-existen guardian (random beneficiary address) + vm.expectRevert(MustBeGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(beneficiary); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Anyone can confirm a revokation after security period + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Friend account is not a guardian anymore + assertEq(openfortAccount.isGuardian(friendAccount), false); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 1); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Anyone can confirm a revokation after security period + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Default account is not a guardian anymore + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 0); + } + + /* + * Test revoking a guardian, but its revocation expired before confirming. + * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD + SECURITY_WINDOW. + */ + function testRevokeEOAGuardianExpired() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD + SECURITY_WINDOW); + vm.expectRevert(PendingRevokeExpired.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Verify that the number of guardians is still 2. No revocation took place + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should still be a guardian + assertEq(openfortAccount.isGuardian(friendAccount), true); + } + + /* + * Test revoking a guardian twice. Make sure a new revocation is not created and the original still works. + * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD and SECURITY_WINDOW. + */ + function testRevokeEOAGuardianDuplicatedPorposal() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + // Friend account should now be a guardian + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) + openfortAccount.revokeGuardian(friendAccount); + + vm.expectRevert(DuplicatedRevoke.selector); + skip(1); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Verify that the number of guardians is now 1 + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian anymore + assertEq(openfortAccount.isGuardian(friendAccount), false); + } + + /* + * Test revoking the default guardian and add it back. + */ + function testRevokeDefaultGuardianAndAddBack() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Verify that the number of guardians is now 0 + assertEq(openfortAccount.guardianCount(), 0); + // deault (openfort) account should not be a guardian anymore + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + + // Expect that we will see an event containing the deault (openfort) account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianProposal(OPENFORT_GUARDIAN); + + // Verify that the number of guardians is now 1 again + assertEq(openfortAccount.guardianCount(), 1); + // deault (openfort) account should be a guardian again + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); + } + + /* + * Test revoking a guardian using owner and cancel before confirming. + * Only the owner can revoke a guardian and cancel its revocation before confirming. + */ + function testCancelRevokeGuardian() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Trying to revoke a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.revokeGuardian(friendAccount); + + // Trying to revoke a non-existen guardian (random beneficiary address) + vm.expectRevert(MustBeGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(beneficiary); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.cancelGuardianRevocation(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationCancelled(friendAccount); + vm.prank(accountAdmin); + openfortAccount.cancelGuardianRevocation(friendAccount); + + // Friend account is not a guardian anymore + assertEq(openfortAccount.isGuardian(friendAccount), true); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 2); + + // Cancelled revocation should not be able to be confirmed now + skip(SECURITY_PERIOD); + vm.expectRevert(UnknownRevoke.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + } + + /* + * Random extra tests to mess up with the logic + */ + function testMessingUpWithGuardianRegister() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Create 4 friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + // Adding and removing guardians + vm.startPrank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + openfortAccount.proposeGuardian(friendAccount2); + vm.stopPrank(); + + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianProposal(friendAccount); + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.confirmGuardianRevocation(friendAccount2); // Notice this tries to confirm a non-existent revocation! + vm.expectRevert(UnknownRevoke.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); // Notice this tries to confirm a non-existent revocation! + vm.prank(accountAdmin); + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.revokeGuardian(friendAccount2); // Notice this tries to revoke a non-existent guardian! + vm.expectRevert(DuplicatedGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); // Notice this tries to register a guardian AGAIN! + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); // Starting a valid revocation process + skip(SECURITY_PERIOD + 1); + vm.expectRevert(DuplicatedGuardian.selector); + openfortAccount.confirmGuardianProposal(friendAccount); // Notice this tries to confirm a guardian that is already valid and pending to revoke! + } + + /** + * Recovery tests * + */ + + /* + * Check the correct functionality of startRecovery() + */ + function testStartRecovery() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.startRecovery(OPENFORT_GUARDIAN); + + vm.prank(OPENFORT_GUARDIAN); + vm.expectRevert(GuardianCannotBeOwner.selector); + openfortAccount.startRecovery(OPENFORT_GUARDIAN); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + + assertEq(openfortAccount.isLocked(), true); + } + + /* + * Checks that incorrect parameters should always fail when trying to complete a recovery + */ + function testBasicChecksCompleteRecovery() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + + assertEq(openfortAccount.isLocked(), true); + + // The recovery time period has not passed. The user should wait to recover. + vm.expectRevert(OngoingRecovery.selector); + bytes[] memory signatures = new bytes[](1); + openfortAccount.completeRecovery(signatures); + + // Providing an empty array when it is expecting one guardian + skip(RECOVERY_PERIOD + 1); + vm.expectRevert(InvalidSignatureAmount.selector); + bytes[] memory signatures_wrong_length = new bytes[](3); + openfortAccount.completeRecovery(signatures_wrong_length); + + // Since signatures are empty, it should return an ECDSA error + vm.expectRevert("ECDSA: invalid signature length"); + openfortAccount.completeRecovery(signatures); + } + + /* + * Most basic, yet complete, recovery flow + * The default Openfort guardian is used to start and complete a recovery process. + * Ownership is transferred to beneficiary + */ + function testBasicCompleteRecovery() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) + ); + + bytes[] memory signatures = new bytes[](1); + signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + + skip(RECOVERY_PERIOD + 1); + openfortAccount.completeRecovery(signatures); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.owner(), address(beneficiary)); + } + + /* + * Case: User added 2 guardians and keeps the default (Openfort) + * The 2 added guardians (friends) are used to recover the account and transfer + * the ownership to beneficiary + * @notice Remember that signatures need to be ordered by the guardian's address. + */ + function test3GuardiansCompleteRecovery() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Create two friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + { + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount2); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + openfortAccount.confirmGuardianProposal(friendAccount2); + } + + { + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + } + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) + ); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + + skip(RECOVERY_PERIOD + 1); + openfortAccount.completeRecovery(signatures); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.owner(), address(beneficiary)); + } + + /* + * Case: User added 2 guardians and keeps the default (Openfort) + * The 2 added guardians (friends) are used to recover the account and transfer + * the ownership to beneficiary. Faild due to unsorted signatures + * @notice Remember that signatures need to be ordered by the guardian's address. + */ + function test3GuardiansUnorderedCompleteRecovery() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Create two friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + { + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount2); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + openfortAccount.confirmGuardianProposal(friendAccount2); + } + + { + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + } + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) + ); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccountPK); // Unsorted! + signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); + + skip(RECOVERY_PERIOD + 1); + vm.expectRevert(InvalidRecoverySignatures.selector); + openfortAccount.completeRecovery(signatures); + + // it should still be locked and the admin still be the same + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.owner(), accountAdmin); + } + + /* + * Case: User added 4 guardians and removes the default (Openfort) + * One guardian (friend) is used to start a recovery process + * The guardian that initiatied the recovery + another one are used to complete the flow. + * @notice Remember that signatures need to be ordered by the guardian's address. + */ + function test4GuardiansNoDefaultCompleteRecovery() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Create 4 friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + // Create 2 more friends. We don't need their PK now as they are not going to sign + address friendAccount3 = makeAddr("friend3"); + address friendAccount4 = makeAddr("friend4"); + + // Adding and removing guardians + { + vm.startPrank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + openfortAccount.proposeGuardian(friendAccount2); + openfortAccount.proposeGuardian(friendAccount3); + openfortAccount.proposeGuardian(friendAccount4); + vm.stopPrank(); + + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianProposal(friendAccount); + openfortAccount.confirmGuardianProposal(friendAccount2); + openfortAccount.confirmGuardianProposal(friendAccount3); + openfortAccount.confirmGuardianProposal(friendAccount4); + + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + } + + // Start the recovery process + { + // Default Openfort guardian tries starts a recovery process because the owner lost the PK + // It should not work as it is not a guardian anymore + vm.expectRevert(MustBeGuardian.selector); + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + vm.prank(friendAccount2); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + } + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) + ); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + + skip(RECOVERY_PERIOD + 1); + openfortAccount.completeRecovery(signatures); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.owner(), address(beneficiary)); + } + + /* + * Case: User added 2 guardians and keeps the default (Openfort) + * The 2 added guardians (friends) are used to recover the account and transfer + * the ownership to beneficiary. Wrong signatures used + * @notice Remember that signatures need to be ordered by the guardian's address. + */ + function test3GuardiansFailCompleteRecovery() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Create two friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + { + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount2); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + openfortAccount.confirmGuardianProposal(friendAccount2); + } + + { + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + } + + // notice: wrong new oner!!! + bytes32 structHash = + keccak256(abi.encode(RECOVER_TYPEHASH, factoryAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + + skip(RECOVERY_PERIOD + 1); + vm.expectRevert(InvalidRecoverySignatures.selector); + openfortAccount.completeRecovery(signatures); + + // it should still be locked and the admin still be the same + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.owner(), accountAdmin); + } + + /* + * Testing the functionality to cancel a recovery process + */ + function testCancelRecovery() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.cancelRecovery(); + + vm.prank(accountAdmin); + openfortAccount.cancelRecovery(); + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) + ); + + bytes[] memory signatures = new bytes[](1); + signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + + skip(RECOVERY_PERIOD + 1); + vm.expectRevert(NoOngoingRecovery.selector); + openfortAccount.completeRecovery(signatures); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.owner(), accountAdmin); + } + + /* + * Testing the startRecovery twice in a row + */ + function testStartRecoveryTwice() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + + // Calling startRecovery() again should revert and have no effect + vm.expectRevert(OngoingRecovery.selector); + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + + // The accounts should still be locked + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.owner(), accountAdmin); + } + + /** + * Transfer ownership tests * + */ + + /* + * Try to transfer ownership to a guardian. + * Should not be allowed. + */ + function testTransferOwnerNotGuardian() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // It should fail as friendAccount is a guardian + vm.expectRevert(GuardianCannotBeOwner.selector); + openfortAccount.transferOwnership(friendAccount); + } + + /* + * Try to transfer ownership to a valid account. + * Should be allowed. + */ + function testTransferOwner() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + + // Create a new owner EOA + address newOwner = makeAddr("newOwner"); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.transferOwnership(newOwner); + + vm.prank(accountAdmin); + openfortAccount.transferOwnership(newOwner); + + vm.prank(newOwner); + openfortAccount.acceptOwnership(); + + // New owner should be now newOwner + assertEq(openfortAccount.owner(), address(newOwner)); + } + + /* + * Temporal test function for coverage purposes showing + * that isGuardianOrGuardianSigner() always returns false. + */ + function testStubFakeMockTempisGuardian() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + assertEq(openfortAccount.isGuardianOrGuardianSigner(OPENFORT_GUARDIAN), false); + } } diff --git a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol b/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol deleted file mode 100644 index f6afa3f..0000000 --- a/test/foundry/core/recoverable/RecoverableOpenfortAccountTest.t.sol +++ /dev/null @@ -1,2363 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -// import {Test, console} from "lib/forge-std/src/Test.sol"; -// import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -// import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; -// import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -// import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -// import {TestToken} from "account-abstraction/test/TestToken.sol"; -// import {RecoverableOpenfortAccount} from "contracts/core/recoverable/RecoverableOpenfortAccount.sol"; -// import {RecoverableOpenfortFactory} from "contracts/core/recoverable/RecoverableOpenfortFactory.sol"; -// import {OpenfortRecoverableProxy} from "contracts/core/recoverable/OpenfortRecoverableProxy.sol"; - -// contract RecoverableOpenfortAccountTest is Test { -// using ECDSA for bytes32; - -// EntryPoint public entryPoint; -// RecoverableOpenfortAccount public recoverableOpenfortAccountImpl; -// RecoverableOpenfortFactory public recoverableOpenfortFactory; -// address public account; -// TestCounter public testCounter; -// TestToken public testToken; - -// // Testing addresses -// address private factoryAdmin; -// uint256 private factoryAdminPKey; - -// address private accountAdmin; -// uint256 private accountAdminPKey; - -// address payable private beneficiary = payable(makeAddr("beneficiary")); - -// uint256 private constant RECOVERY_PERIOD = 36; -// uint256 private constant SECURITY_PERIOD = 24; -// uint256 private constant SECURITY_WINDOW = 12; -// uint256 private constant LOCK_PERIOD = 50; -// address private OPENFORT_GUARDIAN; -// uint256 private OPENFORT_GUARDIAN_PKEY; - -// event AccountCreated(address indexed account, address indexed accountAdmin); -// event GuardianProposed(address indexed guardian, uint256 executeAfter); -// event GuardianProposalCancelled(address indexed guardian); -// event GuardianRevocationRequested(address indexed guardian, uint256 executeAfter); -// event GuardianRevocationCancelled(address indexed guardian); - -// error ZeroAddressNotAllowed(); -// error AccountLocked(); -// error AccountNotLocked(); -// error MustBeGuardian(); -// error DuplicatedGuardian(); -// error UnknownProposal(); -// error PendingProposalNotOver(); -// error PendingProposalExpired(); -// error DuplicatedRevoke(); -// error UnknownRevoke(); -// error PendingRevokeNotOver(); -// error PendingRevokeExpired(); -// error GuardianCannotBeOwner(); -// error NoOngoingRecovery(); -// error OngoingRecovery(); -// error InvalidRecoverySignatures(); -// error InvalidSignatureAmount(); - -// // keccak256("Recover(address recoveryAddress,uint64 executeAfter,uint32 guardiansRequired)"); -// bytes32 private RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; -// bytes32 private constant _TYPE_HASH = -// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - -// /* -// * Auxiliary function to generate a userOP -// */ -// function _setupUserOp( -// address sender, -// uint256 _signerPKey, -// bytes memory _initCode, -// bytes memory _callDataForEntrypoint -// ) internal returns (UserOperation[] memory ops) { -// uint256 nonce = entryPoint.getNonce(sender, 0); - -// // Get user op fields -// UserOperation memory op = UserOperation({ -// sender: sender, -// nonce: nonce, -// initCode: _initCode, -// callData: _callDataForEntrypoint, -// callGasLimit: 500_000, -// verificationGasLimit: 500_000, -// preVerificationGas: 500_000, -// maxFeePerGas: 0, -// maxPriorityFeePerGas: 0, -// paymasterAndData: bytes(""), -// signature: bytes("") -// }); - -// // Sign UserOp -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); -// bytes memory userOpSignature = abi.encodePacked(r, s, v); - -// address recoveredSigner = ECDSA.recover(msgHash, v, r, s); -// address expectedSigner = vm.addr(_signerPKey); -// assertEq(recoveredSigner, expectedSigner); - -// op.signature = userOpSignature; - -// // Store UserOp -// ops = new UserOperation[](1); -// ops[0] = op; -// } - -// /* -// * Auxiliary function to generate a userOP using the execute() -// * from the account -// */ -// function _setupUserOpExecute( -// address sender, -// uint256 _signerPKey, -// bytes memory _initCode, -// address _target, -// uint256 _value, -// bytes memory _callData -// ) internal returns (UserOperation[] memory) { -// bytes memory callDataForEntrypoint = -// abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - -// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); -// } - -// /* -// * Auxiliary function to generate a userOP using the executeBatch() -// * from the account -// */ -// function _setupUserOpExecuteBatch( -// address sender, -// uint256 _signerPKey, -// bytes memory _initCode, -// address[] memory _target, -// uint256[] memory _value, -// bytes[] memory _callData -// ) internal returns (UserOperation[] memory) { -// bytes memory callDataForEntrypoint = -// abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - -// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); -// } - -// /** -// * @notice Initialize the RecoverableOpenfortAccount testing contract. -// * Scenario: -// * - factoryAdmin is the deployer (and owner) of the RecoverableOpenfortFactory -// * - accountAdmin is the account used to deploy new upgradeable accounts -// * - entryPoint is the singleton EntryPoint -// * - testCounter is the counter used to test userOps -// */ -// function setUp() public { -// // Setup and fund signers -// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); -// vm.deal(factoryAdmin, 100 ether); -// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); -// vm.deal(accountAdmin, 100 ether); - -// vm.startPrank(factoryAdmin); -// // If we are in a fork -// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { -// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); -// } -// // If not a fork, deploy entryPoint (at correct address) -// else { -// EntryPoint entryPoint_aux = new EntryPoint(); -// bytes memory code = address(entryPoint_aux).code; -// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); -// vm.etch(targetAddr, code); -// entryPoint = EntryPoint(payable(targetAddr)); -// } -// (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); -// // deploy upgradeable account implementation -// recoverableOpenfortAccountImpl = new RecoverableOpenfortAccount(); -// // deploy upgradeable account factory -// recoverableOpenfortFactory = new RecoverableOpenfortFactory( -// payable(address(entryPoint)), -// address(recoverableOpenfortAccountImpl), -// RECOVERY_PERIOD, -// SECURITY_PERIOD, -// SECURITY_WINDOW, -// LOCK_PERIOD, -// OPENFORT_GUARDIAN -// ); - -// // Create an upgradeable account wallet and get its address -// account = recoverableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); - -// // deploy a new TestCounter -// testCounter = new TestCounter(); -// // deploy a new TestToken (ERC20) -// testToken = new TestToken(); -// vm.stopPrank(); -// } - -// function testCreateWrongFactory() public { -// vm.expectRevert(ZeroAddressNotAllowed.selector); -// vm.prank(factoryAdmin); -// recoverableOpenfortFactory = new RecoverableOpenfortFactory( -// payable(address(entryPoint)), -// address(0), -// RECOVERY_PERIOD, -// SECURITY_PERIOD, -// SECURITY_WINDOW, -// LOCK_PERIOD, -// OPENFORT_GUARDIAN -// ); -// } - -// function testCreateWrongAccount() public { -// vm.expectRevert(ZeroAddressNotAllowed.selector); -// vm.prank(accountAdmin); -// account = address( -// new OpenfortRecoverableProxy{salt: 0}( -// address(recoverableOpenfortAccountImpl), -// abi.encodeCall( -// RecoverableOpenfortAccount.initialize, -// ( -// address(0x0), -// address(entryPoint), -// RECOVERY_PERIOD, -// SECURITY_PERIOD, -// SECURITY_WINDOW, -// LOCK_PERIOD, -// OPENFORT_GUARDIAN) -// ) -// ) -// ); -// } - -// /* -// * Create an account by directly calling the factory. -// */ -// function testCreateAccountWithNonceViaFactory() public { -// // Get the counterfactual address -// vm.prank(factoryAdmin); -// address accountAddress2 = recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, "2"); - -// // Expect that we will see an event containing the account and admin -// vm.expectEmit(true, true, false, true); -// emit AccountCreated(accountAddress2, accountAdmin); - -// // Deploy a upgradeable account to the counterfactual address -// vm.prank(factoryAdmin); -// recoverableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); - -// // Calling it again should just return the address and not create another account -// vm.prank(factoryAdmin); -// recoverableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); - -// // Make sure the counterfactual address has not been altered -// vm.prank(factoryAdmin); -// assertEq(accountAddress2, recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, "2")); -// } - -// /* -// * Create an account calling the factory via EntryPoint. -// * Use initCode -// */ -// function testCreateAccountViaEntryPoint() public { -// // It is not correct to use the Factory using the EntryPoint anymore -// // Accounts created using factories are depend on msg.sender now -// // revert(); - -// // Make sure the smart account does not have any code yet -// address account2 = recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); -// assertEq(account2.code.length, 0); - -// bytes memory initCallData = -// abi.encodeWithSignature("createAccountWithNonce(address,bytes32)", accountAdmin, bytes32("2")); -// bytes memory initCode = abi.encodePacked(abi.encodePacked(address(recoverableOpenfortFactory)), initCallData); - -// UserOperation[] memory userOpCreateAccount = -// _setupUserOpExecute(account2, accountAdminPKey, initCode, address(0), 0, bytes("")); - -// // Expect that we will see an event containing the account and admin -// vm.expectEmit(true, true, false, true); -// emit AccountCreated(account2, accountAdmin); - -// entryPoint.handleOps(userOpCreateAccount, beneficiary); - -// // Make sure the smart account does have some code now -// assert(account2.code.length > 0); - -// // Make sure the counterfactual address has not been altered -// assertEq(account2, recoverableOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); -// } - -// /* -// * Create an account using the factory and make it call count() directly. -// */ -// function testIncrementCounterDirect() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// // Make the admin of the upgradeable account wallet (deployer) call "count" -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).execute( -// address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Create an account by directly calling the factory and make it call count() -// * using the execute() function using the EntryPoint (userOp). Leaveraging ERC-4337. -// */ -// function testIncrementCounterViaEntrypoint() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Create an account by directly calling the factory and make it call count() -// * using the executeBatching() function using the EntryPoint (userOp). Leaveraging ERC-4337. -// */ -// function testIncrementCounterViaEntrypointBatching() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// uint256 count = 3; -// address[] memory targets = new address[](count); -// uint256[] memory values = new uint256[](count); -// bytes[] memory callData = new bytes[](count); - -// for (uint256 i = 0; i < count; i += 1) { -// targets[i] = address(testCounter); -// values[i] = 0; -// callData[i] = abi.encodeWithSignature("count()"); -// } - -// UserOperation[] memory userOp = -// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 3); -// } - -// /* -// * Should fail, try to use a sessionKey that is not registered. -// */ -// function testFailIncrementCounterViaSessionKeyNotregistered() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); -// } - -// /* -// * Use a sessionKey that is registered. -// */ -// function testIncrementCounterViaSessionKey() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); -// address[] memory emptyWhitelist; - -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Register a sessionKey via userOp calling the execute() function -// * using the EntryPoint (userOp). Then use the sessionKey to count -// */ -// function testRegisterSessionKeyViaEntrypoint() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); -// address[] memory emptyWhitelist; - -// UserOperation[] memory userOp = _setupUserOp( -// account, -// accountAdminPKey, -// bytes(""), -// abi.encodeWithSignature( -// "registerSessionKey(address,uint48,uint48,uint48,address[])", -// sessionKey, -// 0, -// 2 ** 48 - 1, -// 100, -// emptyWhitelist -// ) -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); - -// userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Register a master sessionKey via userOp calling the execute() function -// * using the EntryPoint (userOp). Then use that sessionKey to register a second one -// * Should not be allowed: session keys cannot register new session keys! -// */ -// function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); -// address[] memory emptyWhitelist; - -// UserOperation[] memory userOp = _setupUserOp( -// account, -// accountAdminPKey, -// bytes(""), -// abi.encodeWithSignature( -// "registerSessionKey(address,uint48,uint48,uint48,address[])", -// sessionKey, -// 0, -// 2 ** 48 - 1, -// 2 ** 48 - 1, -// emptyWhitelist -// ) -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); - -// userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); - -// address sessionKeyAttack; -// uint256 sessionKeyPrivKeyAttack; -// (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); - -// userOp = _setupUserOp( -// account, -// sessionKeyPrivKey, -// bytes(""), -// abi.encodeWithSignature( -// "registerSessionKey(address,uint48,uint48,uint48,address[])", -// sessionKeyAttack, -// 0, -// 2 ** 48 - 1, -// 2 ** 48 - 1, -// emptyWhitelist -// ) -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); -// } - -// /* -// * Should fail, try to use a sessionKey that is expired. -// */ -// function testIncrementCounterViaSessionKeyExpired() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); -// address[] memory emptyWhitelist; - -// vm.warp(100); -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// vm.expectRevert(); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); -// } - -// /* -// * Should fail, try to use a sessionKey that is revoked. -// */ -// function testFailIncrementCounterViaSessionKeyRevoked() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); -// address[] memory emptyWhitelist; - -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); -// RecoverableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Should fail, try to use a sessionKey that reached its limit. -// */ -// function testFailIncrementCounterViaSessionKeyReachLimit() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); -// address[] memory emptyWhitelist; - -// // We are now in block 100, but our session key is valid until block 150 -// vm.warp(100); -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has only increased by one -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Should fail, try to use a sessionKey that reached its limit. -// */ -// function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); -// address[] memory emptyWhitelist; - -// // We are now in block 100, but our session key is valid until block 150 -// vm.warp(100); -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); - -// uint256 count = 3; -// address[] memory targets = new address[](count); -// uint256[] memory values = new uint256[](count); -// bytes[] memory callData = new bytes[](count); - -// for (uint256 i = 0; i < count; i += 1) { -// targets[i] = address(testCounter); -// values[i] = 0; -// callData[i] = abi.encodeWithSignature("count()"); -// } - -// UserOperation[] memory userOp = -// _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); -// } - -// /* -// * Should fail, try to revoke a sessionKey using a non-privileged user -// */ -// function testFailRevokeSessionKeyInvalidUser() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); -// address[] memory emptyWhitelist; - -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); -// vm.prank(beneficiary); -// RecoverableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Use a sessionKey with whitelisting to call Execute(). -// */ -// function testIncrementCounterViaSessionKeyWhitelisting() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - -// address[] memory whitelist = new address[](1); -// whitelist[0] = address(testCounter); -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Should fail, try to register a sessionKey with a large whitelist. -// */ -// function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - -// address[] memory whitelist = new address[](11); -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); -// } - -// /* -// * Use a sessionKey with whitelisting to call ExecuteBatch(). -// */ -// function testIncrementCounterViaSessionKeyWhitelistingBatch() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - -// address[] memory whitelist = new address[](1); -// whitelist[0] = address(testCounter); -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); - -// // Verify that the registered key is not a MasterKey but has whitelisting -// bool isMasterKey; -// bool isWhitelisted; -// (,,, isMasterKey, isWhitelisted,) = RecoverableOpenfortAccount(payable(account)).sessionKeys(sessionKey); -// assert(!isMasterKey); -// assert(isWhitelisted); - -// uint256 count = 3; -// address[] memory targets = new address[](count); -// uint256[] memory values = new uint256[](count); -// bytes[] memory callData = new bytes[](count); - -// for (uint256 i = 0; i < count; i += 1) { -// targets[i] = address(testCounter); -// values[i] = 0; -// callData[i] = abi.encodeWithSignature("count()"); -// } - -// UserOperation[] memory userOp = -// _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 3); -// } - -// /* -// * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). -// */ -// function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - -// address[] memory whitelist = new address[](1); -// whitelist[0] = address(account); -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Should fail, try to use a sessionKey with invalid whitelisting to call ExecuteBatch(). -// */ -// function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// address sessionKey; -// uint256 sessionKeyPrivKey; -// (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - -// address[] memory whitelist = new address[](1); -// whitelist[0] = address(account); -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); - -// uint256 count = 3; -// address[] memory targets = new address[](count); -// uint256[] memory values = new uint256[](count); -// bytes[] memory callData = new bytes[](count); - -// for (uint256 i = 0; i < count; i += 1) { -// targets[i] = address(testCounter); -// values[i] = 0; -// callData[i] = abi.encodeWithSignature("count()"); -// } - -// UserOperation[] memory userOp = _setupUserOpExecuteBatch( -// account, -// sessionKeyPrivKey, //Sign the userOp using the sessionKey's private key -// bytes(""), -// targets, -// values, -// callData -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); -// } - -// /* -// * Change the owner of an account and call TestCounter directly. -// * Important use-case: -// * 1- accountAdmin is Openfort's master wallet and is managing the account of the user. -// * 2- The user claims the ownership of the account to Openfort so Openfort calls -// * transferOwnership() to the account. -// * 3- The user has to "officially" claim the ownership of the account by directly -// * interacting with the smart contract using the acceptOwnership() function. -// * 4- From now on, the user is the owner of the account and can register and revoke session keys themselves. -// * 5- Test that the new owner can directly interact with the account and make it call the testCounter contract. -// */ -// function testChangeOwnershipAndCountDirect() public { -// address accountAdmin2; -// uint256 accountAdmin2PKey; -// (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); - -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); -// vm.prank(accountAdmin2); -// RecoverableOpenfortAccount(payable(account)).acceptOwnership(); - -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// // Make the admin of the upgradeable account wallet (deployer) call "count" -// vm.prank(accountAdmin2); -// RecoverableOpenfortAccount(payable(account)).execute( -// address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Change the owner of an account and call TestCounter though the Entrypoint -// */ -// function testChangeOwnershipAndCountEntryPoint() public { -// address accountAdmin2; -// uint256 accountAdmin2PKey; -// (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); - -// vm.prank(accountAdmin); -// RecoverableOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); -// vm.prank(accountAdmin2); -// RecoverableOpenfortAccount(payable(account)).acceptOwnership(); - -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Test an account with testToken instead of TestCount. -// */ -// function testMintTokenAccount() public { -// // Verify that the totalSupply is stil 0 -// assertEq(testToken.totalSupply(), 0); - -// // Mint 1 to beneficiary -// testToken.mint(beneficiary, 1); -// assertEq(testToken.totalSupply(), 1); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testToken), -// 0, -// abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// // Verify that the totalSupply has increased -// assertEq(testToken.totalSupply(), 2); -// } - -// /* -// * Test receive native tokens. -// */ -// function testReceiveNativeToken() public { -// assertEq(address(account).balance, 0); - -// vm.prank(accountAdmin); -// (bool success,) = payable(account).call{value: 1000}(""); -// assert(success); -// assertEq(address(account).balance, 1000); -// } - -// /* -// * Transfer native tokens out of an account. -// */ -// function testTransferOutNativeToken() public { -// uint256 value = 1000; - -// assertEq(address(account).balance, 0); -// vm.prank(accountAdmin); -// (bool success,) = payable(account).call{value: value}(""); -// assertEq(address(account).balance, value); -// assert(success); -// assertEq(beneficiary.balance, 0); - -// UserOperation[] memory userOp = -// _setupUserOpExecute(account, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); - -// EntryPoint(entryPoint).handleOps(userOp, beneficiary); -// assertEq(beneficiary.balance, value); -// } - -// /* -// * Basic test of simulateValidation() to check that it always reverts. -// */ -// function testSimulateValidation() public { -// // Verify that the counter is stil set to 0 -// assertEq(testCounter.counters(account), 0); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") -// ); - -// entryPoint.depositTo{value: 1000000000000000000}(account); -// // Expect the simulateValidation() to always revert -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); -// } - -// /* -// * 1- Deploy a factory using the old EntryPoint to create an account. -// * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() -// */ -// function testUpdateEntryPoint() public { -// address oldEntryPoint = address(0x0576a174D229E3cFA37253523E645A78A0C91B57); -// address newEntryPoint = vm.envAddress("ENTRY_POINT_ADDRESS"); -// RecoverableOpenfortFactory recoverableOpenfortFactoryOld = new RecoverableOpenfortFactory( -// payable(oldEntryPoint), -// address(recoverableOpenfortAccountImpl), -// RECOVERY_PERIOD, -// SECURITY_PERIOD, -// SECURITY_WINDOW, -// LOCK_PERIOD, -// OPENFORT_GUARDIAN -// ); - -// // Create an upgradeable account wallet using the old EntryPoint and get its address -// address payable accountOld = payable(recoverableOpenfortFactoryOld.createAccountWithNonce(accountAdmin, "999")); -// RecoverableOpenfortAccount upgradeableAccount = RecoverableOpenfortAccount(accountOld); -// assertEq(address(upgradeableAccount.entryPoint()), oldEntryPoint); - -// vm.expectRevert("Ownable: caller is not the owner"); -// upgradeableAccount.updateEntryPoint(newEntryPoint); - -// vm.expectRevert(ZeroAddressNotAllowed.selector); -// vm.prank(accountAdmin); -// upgradeableAccount.updateEntryPoint(address(0)); - -// vm.prank(accountAdmin); -// upgradeableAccount.updateEntryPoint(newEntryPoint); - -// assertEq(address(upgradeableAccount.entryPoint()), newEntryPoint); -// } - -// /** -// * Lock tests * -// */ - -// /* -// * Test locking the Openfort account using the default guardian. -// */ -// function testLockAccount() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// assertEq(recoverableOpenfortAccount.isLocked(), false); -// assertEq(recoverableOpenfortAccount.getLock(), 0); - -// vm.expectRevert(MustBeGuardian.selector); -// recoverableOpenfortAccount.lock(); - -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.lock(); - -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// assertEq(recoverableOpenfortAccount.getLock(), block.timestamp + LOCK_PERIOD); - -// vm.expectRevert(AccountLocked.selector); -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.lock(); - -// // Automatically unlock -// skip(LOCK_PERIOD + 1); -// assertEq(recoverableOpenfortAccount.isLocked(), false); -// assertEq(recoverableOpenfortAccount.getLock(), 0); -// } - -// /* -// * Test unlocking the Openfort account using the default guardian. -// */ -// function testUnlockAccount() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// assertEq(recoverableOpenfortAccount.isLocked(), false); -// assertEq(recoverableOpenfortAccount.getLock(), 0); - -// vm.expectRevert(MustBeGuardian.selector); -// recoverableOpenfortAccount.lock(); - -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.lock(); - -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// assertEq(recoverableOpenfortAccount.getLock(), block.timestamp + LOCK_PERIOD); - -// skip(LOCK_PERIOD / 2); - -// vm.expectRevert(MustBeGuardian.selector); -// recoverableOpenfortAccount.unlock(); -// assertEq(recoverableOpenfortAccount.isLocked(), true); - -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.unlock(); - -// assertEq(recoverableOpenfortAccount.isLocked(), false); -// assertEq(recoverableOpenfortAccount.getLock(), 0); - -// vm.expectRevert(AccountNotLocked.selector); -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.unlock(); -// } - -// /** -// * Add guardians tests * -// */ - -// /* -// * Test proposing a guardian (by the owner) and accepting it (by the owner). -// * Successfully propose a guardian and confirm it after SECURITY_PERIOD -// */ -// function testAddEOAGuardian() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// recoverableOpenfortAccount.getGuardians(); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Trying to proposa a guardian not using the owner -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - -// // Test if zero address is a guardian -// assertEq(recoverableOpenfortAccount.isGuardian(address(0)), false); - -// skip(1); - -// vm.expectRevert(PendingProposalNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is now 2 -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - -// // Friend account should be a guardian now -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); -// } - -// /* -// * Test proposing a guardian, but its proposal expires before accepting. -// * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. -// */ -// function testAddEOAGuardianExpired() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Trying to proposa a guardian not using the owner -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - -// skip(1); - -// vm.expectRevert(PendingProposalNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// skip(SECURITY_PERIOD + SECURITY_WINDOW); -// vm.expectRevert(PendingProposalExpired.selector); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); -// } - -// /* -// * Test proposing a guardian, but its proposal expires before accepting. Re-add again -// * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. -// */ -// function testAddEOAGuardianExpiredThenReAdd() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Trying to proposa a guardian not using the owner -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - -// skip(1); - -// vm.expectRevert(PendingProposalNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// skip(SECURITY_PERIOD + SECURITY_WINDOW); -// vm.expectRevert(PendingProposalExpired.selector); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - -// /* Let's try it again (re-add) */ -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is now 2 -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); -// } - -// /* -// * Test proposing a guardian twice. Make sure a new proposal is not created and the original still works. -// * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. -// */ -// function testAddEOAGuardianDuplicatedPorposal() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - -// skip(1); - -// vm.expectRevert(); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is now 2 -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); -// } - -// /* -// * Test proposing a guardian and cancel its proposal before accepting or expiring -// * Only the owner can cancel an ongoing proposal. -// */ -// function testAddEOAGuardianCancel() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Trying to proposa a guardian not using the owner -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - -// skip(1); -// vm.expectRevert(PendingProposalNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// skip(SECURITY_PERIOD); -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.cancelGuardianProposal(friendAccount); - -// vm.expectEmit(true, true, false, true); -// emit GuardianProposalCancelled(friendAccount); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.cancelGuardianProposal(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); - -// vm.prank(accountAdmin); -// vm.expectRevert(UnknownProposal.selector); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); -// } - -// /* -// * Test proposing owner as guardian. It should revert. -// * Successfully propose a guardian and confirm it after SECURITY_PERIOD -// */ -// function testAddOwnerAsGuardianNotAllowed() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// recoverableOpenfortAccount.getGuardians(); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Expect revert because the owner cannot be proposed as guardian -// vm.expectRevert(); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(accountAdmin); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Owner account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(accountAdmin), false); - -// // Expect revert because the default guardian cannot be proposed again -// vm.expectRevert(DuplicatedGuardian.selector); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(OPENFORT_GUARDIAN); - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // OPENFORT_GUARDIAN account should stil be a guardian -// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), true); -// } - -// /* -// * Test proposing multiple guardians (by the owner) and accepting them afterwards (by the owner). -// * Successfully propose guardians and confirm them after SECURITY_PERIOD -// */ -// function testAddMultipleEOAGuardians() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// recoverableOpenfortAccount.getGuardians(); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create multiple friend EOAs -// address[] memory friends = new address[](5); -// friends[0] = makeAddr("friend"); -// friends[1] = makeAddr("friend2"); -// friends[2] = makeAddr("friend3"); -// friends[3] = makeAddr("friend4"); -// friends[4] = makeAddr("friend5"); - -// for (uint256 index = 0; index < friends.length; index++) { -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friends[index], block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friends[index]); -// } - -// // Verify that the number of guardians is still 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); -// // Friend account should not be a guardian yet -// assertEq(recoverableOpenfortAccount.isGuardian(friends[0]), false); - -// skip(1); -// skip(SECURITY_PERIOD); - -// for (uint256 index = 0; index < friends.length; index++) { -// recoverableOpenfortAccount.confirmGuardianProposal(friends[index]); -// } - -// // Verify that the number of guardians is now 6 -// assertEq(recoverableOpenfortAccount.guardianCount(), 6); - -// // First friend account should be a guardian now -// assertEq(recoverableOpenfortAccount.isGuardian(friends[0]), true); -// } - -// /** -// * Revoke guardians tests * -// */ - -// /* -// * Test revoking a guardian using owner. -// * Only the owner can revoke a guardian. -// */ -// function testRevokeGuardian() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is now 2 -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - -// // Friend account should be a guardian now -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - -// // Trying to revoke a guardian not using the owner -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.revokeGuardian(friendAccount); - -// // Trying to revoke a non-existen guardian (random beneficiary address) -// vm.expectRevert(MustBeGuardian.selector); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(beneficiary); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(friendAccount); - -// // Anyone can confirm a revokation. However, the security period has not passed yet -// skip(1); -// vm.expectRevert(PendingRevokeNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - -// // Anyone can confirm a revokation after security period -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - -// // Friend account is not a guardian anymore -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); -// // Verify that the number of guardians is 1 again -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); -// } - -// /* -// * Test revoking the default guardian when having registered another (custom) one. -// * Only the owner can revoke a guardian. -// */ -// function testRevokeDefaultGuardian() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// recoverableOpenfortAccount.getGuardians(); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is now 2 -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - -// // Friend account should be a guardian now -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - -// // Trying to revoke a guardian not using the owner -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - -// // Anyone can confirm a revokation. However, the security period has not passed yet -// skip(1); -// vm.expectRevert(PendingRevokeNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - -// // Anyone can confirm a revokation after security period -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - -// // Default account is not a guardian anymore -// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), false); -// // Verify that the number of guardians is 1 again -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); -// } - -// /* -// * Test revoking all guardians using owner. -// * Only the owner can revoke a guardian. -// */ -// function testRevokeAllGuardians() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is now 2 -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - -// // Friend account should be a guardian now -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - -// // Trying to revoke a guardian not using the owner -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.revokeGuardian(friendAccount); - -// // Trying to revoke a non-existen guardian (random beneficiary address) -// vm.expectRevert(MustBeGuardian.selector); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(beneficiary); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(friendAccount); - -// // Anyone can confirm a revokation. However, the security period has not passed yet -// skip(1); -// vm.expectRevert(PendingRevokeNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - -// // Anyone can confirm a revokation after security period -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - -// // Friend account is not a guardian anymore -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); -// // Verify that the number of guardians is 1 again -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - -// // Anyone can confirm a revokation. However, the security period has not passed yet -// skip(1); -// vm.expectRevert(PendingRevokeNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - -// // Anyone can confirm a revokation after security period -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - -// // Default account is not a guardian anymore -// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), false); -// // Verify that the number of guardians is 1 again -// assertEq(recoverableOpenfortAccount.guardianCount(), 0); -// } - -// /* -// * Test revoking a guardian, but its revocation expired before confirming. -// * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD + SECURITY_WINDOW. -// */ -// function testRevokeEOAGuardianExpired() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(friendAccount); - -// skip(1); -// skip(SECURITY_PERIOD + SECURITY_WINDOW); -// vm.expectRevert(PendingRevokeExpired.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - -// // Verify that the number of guardians is still 2. No revocation took place -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - -// // Friend account should still be a guardian -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); -// } - -// /* -// * Test revoking a guardian twice. Make sure a new revocation is not created and the original still works. -// * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD and SECURITY_WINDOW. -// */ -// function testRevokeEOAGuardianDuplicatedPorposal() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is now 2 -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); -// // Friend account should now be a guardian -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) -// recoverableOpenfortAccount.revokeGuardian(friendAccount); - -// vm.expectRevert(DuplicatedRevoke.selector); -// skip(1); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(friendAccount); - -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - -// // Verify that the number of guardians is now 1 -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); -// // Friend account should not be a guardian anymore -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), false); -// } - -// /* -// * Test revoking the default guardian and add it back. -// */ -// function testRevokeDefaultGuardianAndAddBack() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) -// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - -// skip(SECURITY_PERIOD + 1); -// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - -// // Verify that the number of guardians is now 0 -// assertEq(recoverableOpenfortAccount.guardianCount(), 0); -// // deault (openfort) account should not be a guardian anymore -// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), false); - -// // Expect that we will see an event containing the deault (openfort) account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(OPENFORT_GUARDIAN); - -// skip(SECURITY_PERIOD + 1); -// recoverableOpenfortAccount.confirmGuardianProposal(OPENFORT_GUARDIAN); - -// // Verify that the number of guardians is now 1 again -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); -// // deault (openfort) account should be a guardian again -// assertEq(recoverableOpenfortAccount.isGuardian(OPENFORT_GUARDIAN), true); -// } - -// /* -// * Test revoking a guardian using owner and cancel before confirming. -// * Only the owner can revoke a guardian and cancel its revocation before confirming. -// */ -// function testCancelRevokeGuardian() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Verify that the number of guardians is 1 (default) -// assertEq(recoverableOpenfortAccount.guardianCount(), 1); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // Verify that the number of guardians is now 2 -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); -// // Friend account should be a guardian now -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); - -// // Trying to revoke a guardian not using the owner -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.revokeGuardian(friendAccount); - -// // Trying to revoke a non-existen guardian (random beneficiary address) -// vm.expectRevert(MustBeGuardian.selector); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(beneficiary); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(friendAccount); - -// // Anyone can confirm a revokation. However, the security period has not passed yet -// skip(1); -// vm.expectRevert(PendingRevokeNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); - -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.cancelGuardianRevocation(friendAccount); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianRevocationCancelled(friendAccount); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.cancelGuardianRevocation(friendAccount); - -// // Friend account is not a guardian anymore -// assertEq(recoverableOpenfortAccount.isGuardian(friendAccount), true); -// // Verify that the number of guardians is 1 again -// assertEq(recoverableOpenfortAccount.guardianCount(), 2); - -// // Cancelled revocation should not be able to be confirmed now -// skip(SECURITY_PERIOD); -// vm.expectRevert(UnknownRevoke.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); -// } - -// /* -// * Random extra tests to mess up with the logic -// */ -// function testMessingUpWithGuardianRegister() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Create 4 friends -// address friendAccount; -// uint256 friendAccountPK; -// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - -// address friendAccount2; -// uint256 friendAccount2PK; -// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - -// // Adding and removing guardians -// vm.startPrank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); -// recoverableOpenfortAccount.proposeGuardian(friendAccount2); -// vm.stopPrank(); - -// skip(SECURITY_PERIOD + 1); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); -// vm.expectRevert(MustBeGuardian.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount2); // Notice this tries to confirm a non-existent revocation! -// vm.expectRevert(UnknownRevoke.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(friendAccount); // Notice this tries to confirm a non-existent revocation! -// vm.prank(accountAdmin); -// vm.expectRevert(MustBeGuardian.selector); -// recoverableOpenfortAccount.revokeGuardian(friendAccount2); // Notice this tries to revoke a non-existent guardian! -// vm.expectRevert(DuplicatedGuardian.selector); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); // Notice this tries to register a guardian AGAIN! -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(friendAccount); // Starting a valid revocation process -// skip(SECURITY_PERIOD + 1); -// vm.expectRevert(DuplicatedGuardian.selector); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); // Notice this tries to confirm a guardian that is already valid and pending to revoke! -// } - -// /** -// * Recovery tests * -// */ - -// /* -// * Check the correct functionality of startRecovery() -// */ -// function testStartRecovery() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// vm.expectRevert(MustBeGuardian.selector); -// recoverableOpenfortAccount.startRecovery(OPENFORT_GUARDIAN); - -// vm.prank(OPENFORT_GUARDIAN); -// vm.expectRevert(GuardianCannotBeOwner.selector); -// recoverableOpenfortAccount.startRecovery(OPENFORT_GUARDIAN); - -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); - -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// } - -// /* -// * Checks that incorrect parameters should always fail when trying to complete a recovery -// */ -// function testBasicChecksCompleteRecovery() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); - -// assertEq(recoverableOpenfortAccount.isLocked(), true); - -// // The recovery time period has not passed. The user should wait to recover. -// vm.expectRevert(OngoingRecovery.selector); -// bytes[] memory signatures = new bytes[](1); -// recoverableOpenfortAccount.completeRecovery(signatures); - -// // Providing an empty array when it is expecting one guardian -// skip(RECOVERY_PERIOD + 1); -// vm.expectRevert(InvalidSignatureAmount.selector); -// bytes[] memory signatures_wrong_length = new bytes[](3); -// recoverableOpenfortAccount.completeRecovery(signatures_wrong_length); - -// // Since signatures are empty, it should return an ECDSA error -// vm.expectRevert("ECDSA: invalid signature length"); -// recoverableOpenfortAccount.completeRecovery(signatures); -// } - -// /* -// * Auxiliary function to get a valid EIP712 signature using _eip721contract's domains separator, -// * a valid hash of the message to sign (_structHash) and a private key (_pk) -// */ -// function getEIP712SignatureFrom(address _eip721contract, bytes32 _structHash, uint256 _pk) -// internal -// returns (bytes memory signature721) -// { -// (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = -// IERC5267(_eip721contract).eip712Domain(); -// bytes32 domainSeparator = keccak256( -// abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) -// ); -// bytes32 hash712 = domainSeparator.toTypedDataHash(_structHash); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, hash712); -// signature721 = abi.encodePacked(r, s, v); -// assertEq(ecrecover(hash712, v, r, s), vm.addr(_pk)); -// } - -// /* -// * Most basic, yet complete, recovery flow -// * The default Openfort guardian is used to start and complete a recovery process. -// * Ownership is transferred to beneficiary -// */ -// function testBasicCompleteRecovery() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Default Openfort guardian starts a recovery process because the owner lost the PK -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); -// assertEq(recoverableOpenfortAccount.isLocked(), true); - -// bytes32 structHash = keccak256( -// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) -// ); - -// bytes[] memory signatures = new bytes[](1); -// signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); - -// skip(RECOVERY_PERIOD + 1); -// recoverableOpenfortAccount.completeRecovery(signatures); - -// assertEq(recoverableOpenfortAccount.isLocked(), false); -// assertEq(recoverableOpenfortAccount.owner(), address(beneficiary)); -// } - -// /* -// * Case: User added 2 guardians and keeps the default (Openfort) -// * The 2 added guardians (friends) are used to recover the account and transfer -// * the ownership to beneficiary -// * @notice Remember that signatures need to be ordered by the guardian's address. -// */ -// function test3GuardiansCompleteRecovery() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Create two friends -// address friendAccount; -// uint256 friendAccountPK; -// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - -// address friendAccount2; -// uint256 friendAccount2PK; -// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - -// { -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount2); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); -// } - -// { -// // Default Openfort guardian starts a recovery process because the owner lost the PK -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// } - -// bytes32 structHash = keccak256( -// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) -// ); - -// bytes[] memory signatures = new bytes[](2); -// signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address -// signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); - -// skip(RECOVERY_PERIOD + 1); -// recoverableOpenfortAccount.completeRecovery(signatures); - -// assertEq(recoverableOpenfortAccount.isLocked(), false); -// assertEq(recoverableOpenfortAccount.owner(), address(beneficiary)); -// } - -// /* -// * Case: User added 2 guardians and keeps the default (Openfort) -// * The 2 added guardians (friends) are used to recover the account and transfer -// * the ownership to beneficiary. Faild due to unsorted signatures -// * @notice Remember that signatures need to be ordered by the guardian's address. -// */ -// function test3GuardiansUnorderedCompleteRecovery() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Create two friends -// address friendAccount; -// uint256 friendAccountPK; -// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - -// address friendAccount2; -// uint256 friendAccount2PK; -// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - -// { -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount2); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); -// } - -// { -// // Default Openfort guardian starts a recovery process because the owner lost the PK -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// } - -// bytes32 structHash = keccak256( -// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) -// ); - -// bytes[] memory signatures = new bytes[](2); -// signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccountPK); // Unsorted! -// signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); - -// skip(RECOVERY_PERIOD + 1); -// vm.expectRevert(InvalidRecoverySignatures.selector); -// recoverableOpenfortAccount.completeRecovery(signatures); - -// // it should still be locked and the admin still be the same -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// assertEq(recoverableOpenfortAccount.owner(), accountAdmin); -// } - -// /* -// * Case: User added 4 guardians and removes the default (Openfort) -// * One guardian (friend) is used to start a recovery process -// * The guardian that initiatied the recovery + another one are used to complete the flow. -// * @notice Remember that signatures need to be ordered by the guardian's address. -// */ -// function test4GuardiansNoDefaultCompleteRecovery() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Create 4 friends -// address friendAccount; -// uint256 friendAccountPK; -// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - -// address friendAccount2; -// uint256 friendAccount2PK; -// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - -// // Create 2 more friends. We don't need their PK now as they are not going to sign -// address friendAccount3 = makeAddr("friend3"); -// address friendAccount4 = makeAddr("friend4"); - -// // Adding and removing guardians -// { -// vm.startPrank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); -// recoverableOpenfortAccount.proposeGuardian(friendAccount2); -// recoverableOpenfortAccount.proposeGuardian(friendAccount3); -// recoverableOpenfortAccount.proposeGuardian(friendAccount4); -// vm.stopPrank(); - -// skip(SECURITY_PERIOD + 1); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount3); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount4); - -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.revokeGuardian(OPENFORT_GUARDIAN); -// vm.expectRevert(PendingRevokeNotOver.selector); -// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); -// skip(SECURITY_PERIOD + 1); -// recoverableOpenfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); -// } - -// // Start the recovery process -// { -// // Default Openfort guardian tries starts a recovery process because the owner lost the PK -// // It should not work as it is not a guardian anymore -// vm.expectRevert(MustBeGuardian.selector); -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); -// vm.prank(friendAccount2); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// } - -// bytes32 structHash = keccak256( -// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) -// ); - -// bytes[] memory signatures = new bytes[](2); -// signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address -// signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); - -// skip(RECOVERY_PERIOD + 1); -// recoverableOpenfortAccount.completeRecovery(signatures); - -// assertEq(recoverableOpenfortAccount.isLocked(), false); -// assertEq(recoverableOpenfortAccount.owner(), address(beneficiary)); -// } - -// /* -// * Case: User added 2 guardians and keeps the default (Openfort) -// * The 2 added guardians (friends) are used to recover the account and transfer -// * the ownership to beneficiary. Wrong signatures used -// * @notice Remember that signatures need to be ordered by the guardian's address. -// */ -// function test3GuardiansFailCompleteRecovery() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Create two friends -// address friendAccount; -// uint256 friendAccountPK; -// (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); - -// address friendAccount2; -// uint256 friendAccount2PK; -// (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); - -// { -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount2); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount2); -// } - -// { -// // Default Openfort guardian starts a recovery process because the owner lost the PK -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// } - -// // notice: wrong new oner!!! -// bytes32 structHash = -// keccak256(abi.encode(RECOVER_TYPEHASH, factoryAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); - -// bytes[] memory signatures = new bytes[](2); -// signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address -// signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); - -// skip(RECOVERY_PERIOD + 1); -// vm.expectRevert(InvalidRecoverySignatures.selector); -// recoverableOpenfortAccount.completeRecovery(signatures); - -// // it should still be locked and the admin still be the same -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// assertEq(recoverableOpenfortAccount.owner(), accountAdmin); -// } - -// /* -// * Testing the functionality to cancel a recovery process -// */ -// function testCancelRecovery() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Default Openfort guardian starts a recovery process because the owner lost the PK -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); -// assertEq(recoverableOpenfortAccount.isLocked(), true); - -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.cancelRecovery(); - -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.cancelRecovery(); - -// bytes32 structHash = keccak256( -// abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) -// ); - -// bytes[] memory signatures = new bytes[](1); -// signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); - -// skip(RECOVERY_PERIOD + 1); -// vm.expectRevert(NoOngoingRecovery.selector); -// recoverableOpenfortAccount.completeRecovery(signatures); - -// assertEq(recoverableOpenfortAccount.isLocked(), false); -// assertEq(recoverableOpenfortAccount.owner(), accountAdmin); -// } - -// /* -// * Testing the startRecovery twice in a row -// */ -// function testStartRecoveryTwice() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Default Openfort guardian starts a recovery process because the owner lost the PK -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); -// assertEq(recoverableOpenfortAccount.isLocked(), true); - -// // Calling startRecovery() again should revert and have no effect -// vm.expectRevert(OngoingRecovery.selector); -// vm.prank(OPENFORT_GUARDIAN); -// recoverableOpenfortAccount.startRecovery(address(beneficiary)); - -// // The accounts should still be locked -// assertEq(recoverableOpenfortAccount.isLocked(), true); -// assertEq(recoverableOpenfortAccount.owner(), accountAdmin); -// } - -// /** -// * Transfer ownership tests * -// */ - -// /* -// * Try to transfer ownership to a guardian. -// * Should not be allowed. -// */ -// function testTransferOwnerNotGuardian() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Create a friend EOA -// address friendAccount = makeAddr("friend"); - -// // Expect that we will see an event containing the friend account and security period -// vm.expectEmit(true, true, false, true); -// emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.proposeGuardian(friendAccount); - -// skip(1); -// skip(SECURITY_PERIOD); -// recoverableOpenfortAccount.confirmGuardianProposal(friendAccount); - -// // It should fail as friendAccount is a guardian -// vm.expectRevert(GuardianCannotBeOwner.selector); -// recoverableOpenfortAccount.transferOwnership(friendAccount); -// } - -// /* -// * Try to transfer ownership to a valid account. -// * Should be allowed. -// */ -// function testTransferOwner() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); - -// // Create a new owner EOA -// address newOwner = makeAddr("newOwner"); - -// vm.expectRevert("Ownable: caller is not the owner"); -// recoverableOpenfortAccount.transferOwnership(newOwner); - -// vm.prank(accountAdmin); -// recoverableOpenfortAccount.transferOwnership(newOwner); - -// vm.prank(newOwner); -// recoverableOpenfortAccount.acceptOwnership(); - -// // New owner should be now newOwner -// assertEq(recoverableOpenfortAccount.owner(), address(newOwner)); -// } - -// /* -// * Temporal test function for coverage purposes showing -// * that isGuardianOrGuardianSigner() always returns false. -// */ -// function testStubFakeMockTempisGuardian() public { -// RecoverableOpenfortAccount recoverableOpenfortAccount = RecoverableOpenfortAccount(payable(account)); -// assertEq(recoverableOpenfortAccount.isGuardianOrGuardianSigner(OPENFORT_GUARDIAN), false); -// } -// } diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index aa3e742..30a512a 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Test, console} from "lib/forge-std/src/Test.sol"; +import {console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; @@ -11,115 +11,13 @@ import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/Upgradeable import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {OpenfortUpgradeableProxy} from "contracts/core/upgradeable/OpenfortUpgradeableProxy.sol"; import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; +import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; -contract UpgradeableOpenfortAccountTest is Test { +contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; - EntryPoint public entryPoint; UpgradeableOpenfortAccount public upgradeableOpenfortAccount; UpgradeableOpenfortFactory public upgradeableOpenfortFactory; - address public account; - TestCounter public testCounter; - MockERC20 public mockERC20; - - // Testing addresses - address private factoryAdmin; - uint256 private factoryAdminPKey; - - address private accountAdmin; - uint256 private accountAdminPKey; - - address payable private beneficiary = payable(makeAddr("beneficiary")); - - // bytes4(keccak256("isValidSignature(bytes32,bytes)") - bytes4 internal constant MAGICVALUE = 0x1626ba7e; - // keccak256("OpenfortMessage(bytes32 hashedMessage)"); - bytes32 private constant OF_MSG_TYPEHASH = 0x57159f03b9efda178eab2037b2ec0b51ce11be0051b8a2a9992c29dc260e4a30; - bytes32 private constant _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - - event AccountCreated(address indexed account, address indexed accountAdmin); - event AccountImplementationDeployed(address indexed creator); - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (UserOperation[] memory ops) { - uint256 nonce = entryPoint.getNonce(sender, 0); - - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 0, - maxPriorityFeePerGas: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } /** * @notice Initialize the UpgradeableOpenfortAccount testing contract. @@ -130,11 +28,13 @@ contract UpgradeableOpenfortAccountTest is Test { * - testCounter is the counter used to test userOps */ function setUp() public { + versionSalt = bytes32(0x0); // Setup and fund signers (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); vm.deal(factoryAdmin, 100 ether); (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); vm.deal(accountAdmin, 100 ether); + (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); vm.startPrank(factoryAdmin); // If we are in a fork @@ -152,10 +52,18 @@ contract UpgradeableOpenfortAccountTest is Test { // deploy upgradeable account implementation vm.expectEmit(true, true, false, true); emit AccountImplementationDeployed(factoryAdmin); - upgradeableOpenfortAccount = new UpgradeableOpenfortAccount(); + upgradeableOpenfortAccount = new UpgradeableOpenfortAccount{salt: versionSalt}(); // deploy upgradeable account factory - upgradeableOpenfortFactory = - new UpgradeableOpenfortFactory(payable(address(entryPoint)), address(upgradeableOpenfortAccount)); + upgradeableOpenfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( + factoryAdmin, + address(entryPoint), + address(upgradeableOpenfortAccount), + RECOVERY_PERIOD, + SECURITY_PERIOD, + SECURITY_WINDOW, + LOCK_PERIOD, + OPENFORT_GUARDIAN + ); // Create an upgradeable account wallet and get its address account = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); @@ -1058,8 +966,16 @@ contract UpgradeableOpenfortAccountTest is Test { function testUpdateEntryPoint() public { address oldEntryPoint = address(0x0576a174D229E3cFA37253523E645A78A0C91B57); address newEntryPoint = vm.envAddress("ENTRY_POINT_ADDRESS"); - UpgradeableOpenfortFactory upgradeableOpenfortFactoryOld = - new UpgradeableOpenfortFactory(payable(oldEntryPoint), address(upgradeableOpenfortAccount)); + UpgradeableOpenfortFactory upgradeableOpenfortFactoryOld = new UpgradeableOpenfortFactory{salt: versionSalt}( + factoryAdmin, + oldEntryPoint, + address(upgradeableOpenfortAccount), + RECOVERY_PERIOD, + SECURITY_PERIOD, + SECURITY_WINDOW, + LOCK_PERIOD, + OPENFORT_GUARDIAN + ); // Create an upgradeable account wallet using the old EntryPoint and get its address address payable accountOld = payable(upgradeableOpenfortFactoryOld.createAccountWithNonce(accountAdmin, "999")); @@ -1147,4 +1063,1290 @@ contract UpgradeableOpenfortAccountTest is Test { bytes4 valid = UpgradeableOpenfortAccount(payable(account)).isValidSignature(hash, signature); assertEq(valid, MAGICVALUE); // SHOULD PASS } + + /** + * Lock tests * + */ + + /* + * Test locking the Openfort account using the default guardian. + */ + function testLockAccount() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.getLock(), 0); + + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.lock(); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.lock(); + + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.getLock(), block.timestamp + LOCK_PERIOD); + + vm.expectRevert(AccountLocked.selector); + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.lock(); + + // Automatically unlock + skip(LOCK_PERIOD + 1); + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.getLock(), 0); + } + + /* + * Test unlocking the Openfort account using the default guardian. + */ + function testUnlockAccount() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.getLock(), 0); + + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.lock(); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.lock(); + + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.getLock(), block.timestamp + LOCK_PERIOD); + + skip(LOCK_PERIOD / 2); + + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.unlock(); + assertEq(openfortAccount.isLocked(), true); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.unlock(); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.getLock(), 0); + + vm.expectRevert(AccountNotLocked.selector); + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.unlock(); + } + + /** + * Add guardians tests * + */ + + /* + * Test proposing a guardian (by the owner) and accepting it (by the owner). + * Successfully propose a guardian and confirm it after SECURITY_PERIOD + */ + function testAddEOAGuardian() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + openfortAccount.getGuardians(); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Trying to proposa a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.proposeGuardian(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + // Test if zero address is a guardian + assertEq(openfortAccount.isGuardian(address(0)), false); + + skip(1); + + vm.expectRevert(PendingProposalNotOver.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + } + + /* + * Test proposing a guardian, but its proposal expires before accepting. + * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. + */ + function testAddEOAGuardianExpired() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Trying to proposa a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.proposeGuardian(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + + vm.expectRevert(PendingProposalNotOver.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + skip(SECURITY_PERIOD + SECURITY_WINDOW); + vm.expectRevert(PendingProposalExpired.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + } + + /* + * Test proposing a guardian, but its proposal expires before accepting. Re-add again + * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. + */ + function testAddEOAGuardianExpiredThenReAdd() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Trying to proposa a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.proposeGuardian(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + + vm.expectRevert(PendingProposalNotOver.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + skip(SECURITY_PERIOD + SECURITY_WINDOW); + vm.expectRevert(PendingProposalExpired.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + /* Let's try it again (re-add) */ + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), true); + } + + /* + * Test proposing a guardian twice. Make sure a new proposal is not created and the original still works. + * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. + */ + function testAddEOAGuardianDuplicatedPorposal() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + + vm.expectRevert(); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), true); + } + + /* + * Test proposing a guardian and cancel its proposal before accepting or expiring + * Only the owner can cancel an ongoing proposal. + */ + function testAddEOAGuardianCancel() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Trying to proposa a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.proposeGuardian(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + vm.expectRevert(PendingProposalNotOver.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + skip(SECURITY_PERIOD); + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.cancelGuardianProposal(friendAccount); + + vm.expectEmit(true, true, false, true); + emit GuardianProposalCancelled(friendAccount); + vm.prank(accountAdmin); + openfortAccount.cancelGuardianProposal(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + vm.prank(accountAdmin); + vm.expectRevert(UnknownProposal.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + } + + /* + * Test proposing owner as guardian. It should revert. + * Successfully propose a guardian and confirm it after SECURITY_PERIOD + */ + function testAddOwnerAsGuardianNotAllowed() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + openfortAccount.getGuardians(); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Expect revert because the owner cannot be proposed as guardian + vm.expectRevert(); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(accountAdmin); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Owner account should not be a guardian yet + assertEq(openfortAccount.isGuardian(accountAdmin), false); + + // Expect revert because the default guardian cannot be proposed again + vm.expectRevert(DuplicatedGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // OPENFORT_GUARDIAN account should stil be a guardian + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); + } + + /* + * Test proposing multiple guardians (by the owner) and accepting them afterwards (by the owner). + * Successfully propose guardians and confirm them after SECURITY_PERIOD + */ + function testAddMultipleEOAGuardians() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + openfortAccount.getGuardians(); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create multiple friend EOAs + address[] memory friends = new address[](5); + friends[0] = makeAddr("friend"); + friends[1] = makeAddr("friend2"); + friends[2] = makeAddr("friend3"); + friends[3] = makeAddr("friend4"); + friends[4] = makeAddr("friend5"); + + for (uint256 index = 0; index < friends.length; index++) { + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friends[index], block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friends[index]); + } + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friends[0]), false); + + skip(1); + skip(SECURITY_PERIOD); + + for (uint256 index = 0; index < friends.length; index++) { + openfortAccount.confirmGuardianProposal(friends[index]); + } + + // Verify that the number of guardians is now 6 + assertEq(openfortAccount.guardianCount(), 6); + + // First friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friends[0]), true); + } + + /** + * Revoke guardians tests * + */ + + /* + * Test revoking a guardian using owner. + * Only the owner can revoke a guardian. + */ + function testRevokeGuardian() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Trying to revoke a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.revokeGuardian(friendAccount); + + // Trying to revoke a non-existen guardian (random beneficiary address) + vm.expectRevert(MustBeGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(beneficiary); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Anyone can confirm a revokation after security period + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Friend account is not a guardian anymore + assertEq(openfortAccount.isGuardian(friendAccount), false); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 1); + } + + /* + * Test revoking the default guardian when having registered another (custom) one. + * Only the owner can revoke a guardian. + */ + function testRevokeDefaultGuardian() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + openfortAccount.getGuardians(); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Trying to revoke a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Anyone can confirm a revokation after security period + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Default account is not a guardian anymore + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 1); + } + + /* + * Test revoking all guardians using owner. + * Only the owner can revoke a guardian. + */ + function testRevokeAllGuardians() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Trying to revoke a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.revokeGuardian(friendAccount); + + // Trying to revoke a non-existen guardian (random beneficiary address) + vm.expectRevert(MustBeGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(beneficiary); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Anyone can confirm a revokation after security period + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Friend account is not a guardian anymore + assertEq(openfortAccount.isGuardian(friendAccount), false); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 1); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Anyone can confirm a revokation after security period + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Default account is not a guardian anymore + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 0); + } + + /* + * Test revoking a guardian, but its revocation expired before confirming. + * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD + SECURITY_WINDOW. + */ + function testRevokeEOAGuardianExpired() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD + SECURITY_WINDOW); + vm.expectRevert(PendingRevokeExpired.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Verify that the number of guardians is still 2. No revocation took place + assertEq(openfortAccount.guardianCount(), 2); + + // Friend account should still be a guardian + assertEq(openfortAccount.isGuardian(friendAccount), true); + } + + /* + * Test revoking a guardian twice. Make sure a new revocation is not created and the original still works. + * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD and SECURITY_WINDOW. + */ + function testRevokeEOAGuardianDuplicatedPorposal() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + // Friend account should now be a guardian + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) + openfortAccount.revokeGuardian(friendAccount); + + vm.expectRevert(DuplicatedRevoke.selector); + skip(1); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianRevocation(friendAccount); + + // Verify that the number of guardians is now 1 + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian anymore + assertEq(openfortAccount.isGuardian(friendAccount), false); + } + + /* + * Test revoking the default guardian and add it back. + */ + function testRevokeDefaultGuardianAndAddBack() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + + // Verify that the number of guardians is now 0 + assertEq(openfortAccount.guardianCount(), 0); + // deault (openfort) account should not be a guardian anymore + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + + // Expect that we will see an event containing the deault (openfort) account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianProposal(OPENFORT_GUARDIAN); + + // Verify that the number of guardians is now 1 again + assertEq(openfortAccount.guardianCount(), 1); + // deault (openfort) account should be a guardian again + assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); + } + + /* + * Test revoking a guardian using owner and cancel before confirming. + * Only the owner can revoke a guardian and cancel its revocation before confirming. + */ + function testCancelRevokeGuardian() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // Verify that the number of guardians is now 2 + assertEq(openfortAccount.guardianCount(), 2); + // Friend account should be a guardian now + assertEq(openfortAccount.isGuardian(friendAccount), true); + + // Trying to revoke a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.revokeGuardian(friendAccount); + + // Trying to revoke a non-existen guardian (random beneficiary address) + vm.expectRevert(MustBeGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(beneficiary); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); + + // Anyone can confirm a revokation. However, the security period has not passed yet + skip(1); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.cancelGuardianRevocation(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianRevocationCancelled(friendAccount); + vm.prank(accountAdmin); + openfortAccount.cancelGuardianRevocation(friendAccount); + + // Friend account is not a guardian anymore + assertEq(openfortAccount.isGuardian(friendAccount), true); + // Verify that the number of guardians is 1 again + assertEq(openfortAccount.guardianCount(), 2); + + // Cancelled revocation should not be able to be confirmed now + skip(SECURITY_PERIOD); + vm.expectRevert(UnknownRevoke.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); + } + + /* + * Random extra tests to mess up with the logic + */ + function testMessingUpWithGuardianRegister() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Create 4 friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + // Adding and removing guardians + vm.startPrank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + openfortAccount.proposeGuardian(friendAccount2); + vm.stopPrank(); + + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianProposal(friendAccount); + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.confirmGuardianRevocation(friendAccount2); // Notice this tries to confirm a non-existent revocation! + vm.expectRevert(UnknownRevoke.selector); + openfortAccount.confirmGuardianRevocation(friendAccount); // Notice this tries to confirm a non-existent revocation! + vm.prank(accountAdmin); + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.revokeGuardian(friendAccount2); // Notice this tries to revoke a non-existent guardian! + vm.expectRevert(DuplicatedGuardian.selector); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); // Notice this tries to register a guardian AGAIN! + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(friendAccount); // Starting a valid revocation process + skip(SECURITY_PERIOD + 1); + vm.expectRevert(DuplicatedGuardian.selector); + openfortAccount.confirmGuardianProposal(friendAccount); // Notice this tries to confirm a guardian that is already valid and pending to revoke! + } + + /** + * Recovery tests * + */ + + /* + * Check the correct functionality of startRecovery() + */ + function testStartRecovery() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + vm.expectRevert(MustBeGuardian.selector); + openfortAccount.startRecovery(OPENFORT_GUARDIAN); + + vm.prank(OPENFORT_GUARDIAN); + vm.expectRevert(GuardianCannotBeOwner.selector); + openfortAccount.startRecovery(OPENFORT_GUARDIAN); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + + assertEq(openfortAccount.isLocked(), true); + } + + /* + * Checks that incorrect parameters should always fail when trying to complete a recovery + */ + function testBasicChecksCompleteRecovery() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + + assertEq(openfortAccount.isLocked(), true); + + // The recovery time period has not passed. The user should wait to recover. + vm.expectRevert(OngoingRecovery.selector); + bytes[] memory signatures = new bytes[](1); + openfortAccount.completeRecovery(signatures); + + // Providing an empty array when it is expecting one guardian + skip(RECOVERY_PERIOD + 1); + vm.expectRevert(InvalidSignatureAmount.selector); + bytes[] memory signatures_wrong_length = new bytes[](3); + openfortAccount.completeRecovery(signatures_wrong_length); + + // Since signatures are empty, it should return an ECDSA error + vm.expectRevert("ECDSA: invalid signature length"); + openfortAccount.completeRecovery(signatures); + } + + /* + * Most basic, yet complete, recovery flow + * The default Openfort guardian is used to start and complete a recovery process. + * Ownership is transferred to beneficiary + */ + function testBasicCompleteRecovery() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) + ); + + bytes[] memory signatures = new bytes[](1); + signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + + skip(RECOVERY_PERIOD + 1); + openfortAccount.completeRecovery(signatures); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.owner(), address(beneficiary)); + } + + /* + * Case: User added 2 guardians and keeps the default (Openfort) + * The 2 added guardians (friends) are used to recover the account and transfer + * the ownership to beneficiary + * @notice Remember that signatures need to be ordered by the guardian's address. + */ + function test3GuardiansCompleteRecovery() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Create two friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + { + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount2); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + openfortAccount.confirmGuardianProposal(friendAccount2); + } + + { + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + } + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) + ); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + + skip(RECOVERY_PERIOD + 1); + openfortAccount.completeRecovery(signatures); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.owner(), address(beneficiary)); + } + + /* + * Case: User added 2 guardians and keeps the default (Openfort) + * The 2 added guardians (friends) are used to recover the account and transfer + * the ownership to beneficiary. Faild due to unsorted signatures + * @notice Remember that signatures need to be ordered by the guardian's address. + */ + function test3GuardiansUnorderedCompleteRecovery() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Create two friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + { + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount2); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + openfortAccount.confirmGuardianProposal(friendAccount2); + } + + { + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + } + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) + ); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccountPK); // Unsorted! + signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); + + skip(RECOVERY_PERIOD + 1); + vm.expectRevert(InvalidRecoverySignatures.selector); + openfortAccount.completeRecovery(signatures); + + // it should still be locked and the admin still be the same + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.owner(), accountAdmin); + } + + /* + * Case: User added 4 guardians and removes the default (Openfort) + * One guardian (friend) is used to start a recovery process + * The guardian that initiatied the recovery + another one are used to complete the flow. + * @notice Remember that signatures need to be ordered by the guardian's address. + */ + function test4GuardiansNoDefaultCompleteRecovery() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Create 4 friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + // Create 2 more friends. We don't need their PK now as they are not going to sign + address friendAccount3 = makeAddr("friend3"); + address friendAccount4 = makeAddr("friend4"); + + // Adding and removing guardians + { + vm.startPrank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + openfortAccount.proposeGuardian(friendAccount2); + openfortAccount.proposeGuardian(friendAccount3); + openfortAccount.proposeGuardian(friendAccount4); + vm.stopPrank(); + + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianProposal(friendAccount); + openfortAccount.confirmGuardianProposal(friendAccount2); + openfortAccount.confirmGuardianProposal(friendAccount3); + openfortAccount.confirmGuardianProposal(friendAccount4); + + vm.prank(accountAdmin); + openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + vm.expectRevert(PendingRevokeNotOver.selector); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + skip(SECURITY_PERIOD + 1); + openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + } + + // Start the recovery process + { + // Default Openfort guardian tries starts a recovery process because the owner lost the PK + // It should not work as it is not a guardian anymore + vm.expectRevert(MustBeGuardian.selector); + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + vm.prank(friendAccount2); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + } + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(2)) + ); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + + skip(RECOVERY_PERIOD + 1); + openfortAccount.completeRecovery(signatures); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.owner(), address(beneficiary)); + } + + /* + * Case: User added 2 guardians and keeps the default (Openfort) + * The 2 added guardians (friends) are used to recover the account and transfer + * the ownership to beneficiary. Wrong signatures used + * @notice Remember that signatures need to be ordered by the guardian's address. + */ + function test3GuardiansFailCompleteRecovery() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Create two friends + address friendAccount; + uint256 friendAccountPK; + (friendAccount, friendAccountPK) = makeAddrAndKey("friend"); + + address friendAccount2; + uint256 friendAccount2PK; + (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); + + { + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount2); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + openfortAccount.confirmGuardianProposal(friendAccount2); + } + + { + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + } + + // notice: wrong new oner!!! + bytes32 structHash = + keccak256(abi.encode(RECOVER_TYPEHASH, factoryAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + + skip(RECOVERY_PERIOD + 1); + vm.expectRevert(InvalidRecoverySignatures.selector); + openfortAccount.completeRecovery(signatures); + + // it should still be locked and the admin still be the same + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.owner(), accountAdmin); + } + + /* + * Testing the functionality to cancel a recovery process + */ + function testCancelRecovery() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.cancelRecovery(); + + vm.prank(accountAdmin); + openfortAccount.cancelRecovery(); + + bytes32 structHash = keccak256( + abi.encode(RECOVER_TYPEHASH, address(beneficiary), uint64(block.timestamp + RECOVERY_PERIOD), uint32(1)) + ); + + bytes[] memory signatures = new bytes[](1); + signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + + skip(RECOVERY_PERIOD + 1); + vm.expectRevert(NoOngoingRecovery.selector); + openfortAccount.completeRecovery(signatures); + + assertEq(openfortAccount.isLocked(), false); + assertEq(openfortAccount.owner(), accountAdmin); + } + + /* + * Testing the startRecovery twice in a row + */ + function testStartRecoveryTwice() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Default Openfort guardian starts a recovery process because the owner lost the PK + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + assertEq(openfortAccount.isLocked(), true); + + // Calling startRecovery() again should revert and have no effect + vm.expectRevert(OngoingRecovery.selector); + vm.prank(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(address(beneficiary)); + + // The accounts should still be locked + assertEq(openfortAccount.isLocked(), true); + assertEq(openfortAccount.owner(), accountAdmin); + } + + /** + * Transfer ownership tests * + */ + + /* + * Try to transfer ownership to a guardian. + * Should not be allowed. + */ + function testTransferOwnerNotGuardian() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(accountAdmin); + openfortAccount.proposeGuardian(friendAccount); + + skip(1); + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(friendAccount); + + // It should fail as friendAccount is a guardian + vm.expectRevert(GuardianCannotBeOwner.selector); + openfortAccount.transferOwnership(friendAccount); + } + + /* + * Try to transfer ownership to a valid account. + * Should be allowed. + */ + function testTransferOwner() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + + // Create a new owner EOA + address newOwner = makeAddr("newOwner"); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.transferOwnership(newOwner); + + vm.prank(accountAdmin); + openfortAccount.transferOwnership(newOwner); + + vm.prank(newOwner); + openfortAccount.acceptOwnership(); + + // New owner should be now newOwner + assertEq(openfortAccount.owner(), address(newOwner)); + } + + /* + * Temporal test function for coverage purposes showing + * that isGuardianOrGuardianSigner() always returns false. + */ + function testStubFakeMockTempisGuardian() public { + UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + assertEq(openfortAccount.isGuardianOrGuardianSigner(OPENFORT_GUARDIAN), false); + } } diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index 1a564a0..4814af7 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -12,1683 +12,1683 @@ import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/Upgradeable import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {OpenfortPaymasterV2} from "contracts/paymaster/OpenfortPaymasterV2.sol"; -contract OpenfortPaymasterV2Test is Test { - using ECDSA for bytes32; - - EntryPoint public entryPoint; - UpgradeableOpenfortAccount public staticOpenfortAccount; - UpgradeableOpenfortFactory public staticOpenfortFactory; - OpenfortPaymasterV2 public openfortPaymaster; - address public account; - TestCounter public testCounter; - TestToken public testToken; - - // Testing addresses - address private factoryAdmin; - uint256 private factoryAdminPKey; - - address private paymasterAdmin; - uint256 private paymasterAdminPKey; - - address private accountAdmin; - uint256 private accountAdminPKey; - - address payable private beneficiary = payable(makeAddr("beneficiary")); - - uint48 internal constant VALIDUNTIL = 2 ** 48 - 1; - uint48 internal constant VALIDAFTER = 0; - uint256 internal constant EXCHANGERATE = 10 ** 3; - uint256 internal constant MOCKSIG = 2 ** 256 - 1; - uint256 internal TESTTOKEN_ACCOUNT_PREFUND = 100 * 10 ** 18; - - error InvalidTokenRecipient(); - - event PostOpGasUpdated(uint256 oldPostOpGas, uint256 _newPostOpGas); - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint, - bytes memory paymasterAndData - ) internal returns (UserOperation[] memory ops) { - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: entryPoint.getNonce(sender, 0), - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 1500000000, - maxPriorityFeePerGas: 1500000000, - paymasterAndData: paymasterAndData, - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - // address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - // address expectedSigner = vm.addr(_signerPKey); - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(_signerPKey)); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData, - bytes memory paymasterAndData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData, - bytes memory paymasterAndData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); - } - - function mockedPaymasterDataNative(address _depositor) internal pure returns (bytes memory dataEncoded) { - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; - strategy.depositor = _depositor; - strategy.erc20Token = address(0); - strategy.exchangeRate = 0; - // Looking at the source code, I've found this part was not Packed (filled with 0s) - dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); - } - - function mockedPaymasterDataERC20Dynamic(address _depositor) internal view returns (bytes memory dataEncoded) { - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; - strategy.depositor = _depositor; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - // Looking at the source code, I've found this part was not Packed (filled with 0s) - dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); - } - - function mockedPaymasterDataERC20Fixed(address _depositor, uint256 _pricePerTransaction) - internal - view - returns (bytes memory dataEncoded) - { - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; - strategy.depositor = _depositor; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = _pricePerTransaction; - // Looking at the source code, I've found this part was not Packed (filled with 0s) - dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); - } - - /** - * @notice Initialize the UpgradeableOpenfortAccount testing contract. - * Scenario: - * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory - * - paymasterAdmin is the deployer (and owner) of the OpenfortPaymaster - * - accountAdmin is the account used to deploy new static accounts - * - entryPoint is the singleton EntryPoint - * - testCounter is the counter used to test userOps - */ - function setUp() public { - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - (paymasterAdmin, paymasterAdminPKey) = makeAddrAndKey("paymasterAdmin"); - vm.deal(paymasterAdmin, 100 ether); - - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } - vm.prank(paymasterAdmin); - openfortPaymaster = new OpenfortPaymasterV2(IEntryPoint(payable(address(entryPoint))), paymasterAdmin); - // Paymaster deposits 50 ETH to EntryPoint - vm.prank(paymasterAdmin); - openfortPaymaster.deposit{value: 50 ether}(); - // Paymaster stakes 25 ETH - vm.prank(paymasterAdmin); - openfortPaymaster.addStake{value: 25 ether}(1); - - // deploy account factory - vm.prank(factoryAdmin); - staticOpenfortAccount = new UpgradeableOpenfortAccount(); - vm.prank(factoryAdmin); - staticOpenfortFactory = - new UpgradeableOpenfortFactory((payable(vm.envAddress("ENTRY_POINT_ADDRESS"))), address(staticOpenfortAccount)); - // deploy a new TestCounter - testCounter = new TestCounter(); - // deploy a new TestToken (ERC20) and mint 1000 - testToken = new TestToken(); - testToken.mint(address(this), 1_000 * 10 ** 18); - - // Create an static account wallet and get its address - vm.prank(factoryAdmin); - account = staticOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); - } - - /* - * Test initial parameters - * - */ - function testInitialParameters() public { - assertEq(address(openfortPaymaster.entryPoint()), vm.envAddress("ENTRY_POINT_ADDRESS")); - assertEq(address(openfortPaymaster.owner()), paymasterAdmin); - } - - /** - * Deposit should fail if not the owner - */ - function testFailDeposit() public { - vm.prank(factoryAdmin); - openfortPaymaster.deposit{value: 50 ether}(); - } - - /* - * Test parsePaymasterAndData() when using the native token - * - */ - function testParsePaymasterDataNative() public { - // Encode the paymaster data - bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); - - // Get the related paymaster data signature - bytes32 hash = keccak256(dataEncoded); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - - // Create the paymasterAndData info - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - - ( - uint48 returnedValidUntil, - uint48 returnedValidAfter, - OpenfortPaymasterV2.PolicyStrategy memory strategy, - bytes memory returnedSignature - ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); - - assertEq(returnedValidUntil, VALIDUNTIL); - assertEq(returnedValidAfter, VALIDAFTER); - assertEq(strategy.erc20Token, address(0)); - assertEq(strategy.exchangeRate, 0); - assertEq(signature, returnedSignature); - } - - /* - * Test parsePaymasterAndData() with an ERC20 dynamic - * - */ - function testParsePaymasterDataERC20() public { - // Encode the paymaster data - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - - // Get the related paymaster data signature - bytes32 hash = keccak256(dataEncoded); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - - // Create the paymasterAndData info - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - console.logBytes(paymasterAndData); - - ( - uint48 returnedValidUntil, - uint48 returnedValidAfter, - OpenfortPaymasterV2.PolicyStrategy memory strategy, - bytes memory returnedSignature - ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); - assertEq(returnedValidUntil, VALIDUNTIL); - assertEq(returnedValidAfter, VALIDAFTER); - assertEq(strategy.erc20Token, address(testToken)); - assertEq(strategy.exchangeRate, EXCHANGERATE); - assertEq(signature, returnedSignature); - } - - /* - * The owner (paymasterAdmin) can add and withdraw stake. - * Others cannot. - */ - function testPaymasterStake() public { - assertEq(paymasterAdmin.balance, 25 ether); - - // The owner can add stake - vm.prank(paymasterAdmin); - openfortPaymaster.addStake{value: 2 ether}(10); - assertEq(paymasterAdmin.balance, 23 ether); - - // Others cannot add stake - vm.expectRevert("Ownable: caller is not the owner"); - openfortPaymaster.addStake{value: 2}(10); - - // The owner trying to withdraw stake fails because it has not unlocked - // The owner can withdraw stake - vm.prank(paymasterAdmin); - vm.expectRevert(); - openfortPaymaster.withdrawStake(payable(paymasterAdmin)); - - // The owner unlocks the stake - vm.prank(paymasterAdmin); - openfortPaymaster.unlockStake(); - - // The owner trying to unlock fails because it has not passed enought time - vm.prank(paymasterAdmin); - vm.expectRevert(); - openfortPaymaster.withdrawStake(payable(paymasterAdmin)); - - // Passes 20 blocks... - skip(20); - - // The owner can now withdraw stake (the 2 ethers recently staked + the 25 from the SetUp) - vm.prank(paymasterAdmin); - openfortPaymaster.withdrawStake(payable(paymasterAdmin)); - assertEq(paymasterAdmin.balance, 50 ether); - } - - /* - * Complete deposit walkthrough test - */ - function testDepositsToPaymaster() public { - // Initially, the Paymaster has 50 ether deposited - assertEq(entryPoint.balanceOf(address(openfortPaymaster)), 50 ether); - - // Directly deposit 1 ETH to EntryPoint on behalf of the Paymaster - entryPoint.depositTo{value: 1 ether}(address(openfortPaymaster)); - assertEq(entryPoint.balanceOf(address(openfortPaymaster)), 51 ether); - - // Cannot deposit to address 0 - vm.expectRevert(); - openfortPaymaster.depositFor{value: 0 ether}(address(0)); - - // Cannot deposit 0 ether - vm.expectRevert(); - openfortPaymaster.depositFor{value: 0 ether}(factoryAdmin); - - // Cannot depositFor using owner - vm.prank(paymasterAdmin); - vm.expectRevert(); - openfortPaymaster.depositFor{value: 1 ether}(paymasterAdmin); - - // Paymaster deposits 1 ETH to EntryPoint - openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); - - // Get the WHOLE deposited amount so far - assertEq(openfortPaymaster.getDeposit(), 52 ether); - // Notice that, even though the deposit was made to the EntryPoint for the openfortPaymaster, the deposit is 0: - assertEq(openfortPaymaster.getDepositFor(address(openfortPaymaster)), 0 ether); - - // All deposit not made using "depositFor" goes to owner (paymasterAdmin) - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 51 ether); - - vm.expectRevert("Not Owner: use depositFor() instead"); - openfortPaymaster.deposit{value: 1 ether}(); - // Get the WHOLE deposited amount so far - assertEq(openfortPaymaster.getDeposit(), 52 ether); - - vm.prank(paymasterAdmin); - openfortPaymaster.deposit{value: 1 ether}(); - // Get the WHOLE deposited amount so far - assertEq(openfortPaymaster.getDeposit(), 53 ether); - - vm.expectRevert(); - openfortPaymaster.withdrawTo(payable(paymasterAdmin), 1 ether); - assertEq(openfortPaymaster.getDeposit(), 53 ether); - - vm.prank(paymasterAdmin); - openfortPaymaster.withdrawTo(payable(paymasterAdmin), 1 ether); - // Get the WHOLE deposited amount so far - assertEq(openfortPaymaster.getDeposit(), 52 ether); - - vm.expectRevert(); - vm.prank(paymasterAdmin); - openfortPaymaster.withdrawTo(payable(paymasterAdmin), 10000 ether); - // Get the WHOLE deposited amount so far - assertEq(openfortPaymaster.getDeposit(), 52 ether); - - // Let's now use withdrawDepositorTo - // Owner cannot call it - vm.expectRevert(); - vm.prank(paymasterAdmin); - openfortPaymaster.withdrawDepositorTo(payable(paymasterAdmin), 1 ether); - - // factoryAdmin can call it - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); - assertEq(factoryAdmin.balance, 100 ether); - // but not too much! - vm.expectRevert(); - vm.prank(factoryAdmin); - openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1000 ether); - // not using address 0! - vm.expectRevert(); - vm.prank(factoryAdmin); - openfortPaymaster.withdrawDepositorTo(payable(address(0)), 1 ether); - vm.prank(factoryAdmin); - openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); - assertEq(factoryAdmin.balance, 101 ether); - // Deposit of the owner is still 51 - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 51 ether); - - // Finally, let's test withdrawFromDepositor - // deposit again using factoryAdmin - openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); - // Only owner cannot call it - vm.expectRevert(); - openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); - - vm.expectRevert(); - vm.prank(paymasterAdmin); - openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 100 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); - - vm.prank(paymasterAdmin); - openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); - } - - /* - * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig length) - * Should revert - */ - function testPaymasterUserOpWrongSigLength() public { - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, "0x1234"); // This part was packed (not filled with 0s) - - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testCounter), - 0, - abi.encodeWithSignature("count()"), - paymasterAndData - ); - - // "ECDSA: invalid signature length" - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig) - * Should revert - */ - function testPaymasterUserOpWrongSig() public { - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); // MOCKSIG, "1", MOCKSIG to make sure we send 65 bytes as sig - UserOperation[] memory userOp = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testCounter), - 0, - abi.encodeWithSignature("count()"), - paymasterAndData - ); - - // "AA33 reverted: ECDSA: invalid signature" - vm.expectRevert(); - entryPoint.simulateValidation(userOp[0]); - - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * Should work - */ - function testPaymasterUserOpNativeValidSig() public { - bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testCounter), - 0, - abi.encodeWithSignature("count()"), - paymasterAndData - ); - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(0); - strategy.exchangeRate = 0; - bytes32 hash; - { - // Simulating that the Paymaster gets the userOp and signs it - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - // entryPoint.simulateValidation(userOp); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - //Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with signature from a wrong address - * Should not work - */ - function testPaymasterUserOpNativeWrongUserSig() public { - bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testCounter), - 0, - abi.encodeWithSignature("count()"), - paymasterAndData - ); - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; - strategy.erc20Token = address(0); - strategy.exchangeRate = EXCHANGERATE; - bytes32 hash; - { - // Simulating that the factory admin gets the userOp and tries to sign it - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(factoryAdmin, ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(factoryAdminPKey)); - - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - vm.expectRevert(); - entryPoint.handleOps(userOps, beneficiary); - // entryPoint.simulateValidation(userOp); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); - //Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * Using ERC20. Should work - */ - function testPaymasterUserOpERC20ValidSigDiffMaxPriorityFeePerGas() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), - paymasterAndData - ); - - userOps[0].maxPriorityFeePerGas += 1; - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has decreased - assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - } - - /* - * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) - * Using ERC20. Should work - */ - function testPaymasterUserOpERC20ValidSig() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), - paymasterAndData - ); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has decreased - assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * Using FIXED ERC20. Should work - */ - function testPaymasterUserOpERC20FixedValidSig() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - uint256 pricePerTransaction = 10 ** 18; - - bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), - paymasterAndData - ); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = pricePerTransaction; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has decreased - assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Using dynamic ERC20. Should work - */ - function testPaymasterUserOpERC20ValidSigExecBatch() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - assertEq(testCounter.counters(account), 0); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has decreased - assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Using fixed ERC20. Should work - */ - function testPaymasterUserOpERC20FixedValidSigExecBatch() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - assertEq(testCounter.counters(account), 0); - - uint256 pricePerTransaction = 10 ** 18; - - bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = pricePerTransaction; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has decreased - assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Using fixed ERC20 expensive. Should work - */ - function testPaymasterUserOpERC20FixedExpensiveValidSigExecBatch() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - assertEq(testCounter.counters(account), 0); - - uint256 pricePerTransaction = 10; - - bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = pricePerTransaction; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has decreased - assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Should work - */ - function testPaymasterUserOpNativeValidSigExecBatch() public { - bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(0); - strategy.exchangeRate = 0; - - bytes32 hash; - { - // Simulating that the Paymaster gets the userOp and signs it - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - // entryPoint.simulateValidation(userOp); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - //Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * ExecBatch. Using ERC20. Should work. - * Test showing that failing to repay in ERC20 still spends some of Paymaster's deposit (DoS) - */ - function testFailPaymasterUserOpERC20ValidSigExecBatchInsufficientERC20() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, 100); - assertEq(testToken.balanceOf(account), 100); - - assertEq(testCounter.counters(account), 0); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - uint256 count = 2; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - targets[0] = address(testToken); - values[0] = 0; - callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - - targets[1] = address(testCounter); - values[1] = 0; - callData[1] = abi.encodeWithSignature("count()"); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has the same deposit - assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has not decreased - assertEq(testToken.balanceOf(account), 100); - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - - // If this fails, it would mean: - // 1- That the paymaster has spent some of its deposit - // 2- That the smart account could not perform the desired actions, but still has all testTokens - // An attacker could DoS the paymaster to drain its deposit - } - - /* - * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) - * Using ERC20. Should work. - */ - function testFailPaymasterUserOpERC20ValidSigSmallApprove() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 1), - paymasterAndData - ); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; - strategy.depositor = paymasterAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has the same deposit - assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has not decreased - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); - - // If this fails, it would mean: - // 1- That the paymaster has spent some of its deposit - // 2- That the smart account could not perform the desired actions, but still has all testTokens - // An attacker could DoS the paymaster to drain its deposit - } - - /* - * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) - * Using ERC20 and a 3rd party depositor. Should work - */ - function testPaymasterUserOpERC20ValidSigDepositor() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - vm.prank(factoryAdmin); - openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); - assertEq(openfortPaymaster.getDeposit(), 100 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - - bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(factoryAdmin); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), - paymasterAndData - ); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; - strategy.depositor = factoryAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = EXCHANGERATE; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has decreased - assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - - assert(openfortPaymaster.getDeposit() < 100 ether); - assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); - assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // deposit of the owner should have increased a bit because of the dust - } - - /* - * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) - * Using ERC20 (fixed rate) and a 3rd party depositor. Should work - */ - function testPaymasterUserOpERC20FixedValidSigDepositor() public { - assertEq(testToken.balanceOf(account), 0); - testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - - vm.prank(factoryAdmin); - openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); - assertEq(openfortPaymaster.getDeposit(), 100 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - - uint256 pricePerTransaction = 10 ** 18; - - bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(factoryAdmin, pricePerTransaction); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - - // Create a userOp to let the paymaster use our testTokens - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testToken), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), - paymasterAndData - ); - - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; - strategy.depositor = factoryAdmin; - strategy.erc20Token = address(testToken); - strategy.exchangeRate = pricePerTransaction; - - // Simulating that the Paymaster gets the userOp and signs it - bytes32 hash; - { - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - // Verify that the balance of the smart account has decreased - assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - - assert(openfortPaymaster.getDeposit() < 100 ether); - assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); - assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // deposit of the owner should have increased a bit because of the dust - } - - /* - * Test setPostOpGas function - */ - function testSetPostOpGas() public { - vm.expectRevert("Ownable: caller is not the owner"); - openfortPaymaster.setPostOpGas(15_000); - - vm.prank(paymasterAdmin); - vm.expectRevert(); - openfortPaymaster.setPostOpGas(0); - - // Expect that we will see a PostOpGasUpdated event - vm.prank(paymasterAdmin); - vm.expectEmit(true, true, false, false); - emit PostOpGasUpdated(40_000, 15_000); - openfortPaymaster.setPostOpGas(15_000); - } - - /* - * Trigger _requireFromEntryPoint() from BaseOpenfortPaymaster - */ - function test_requireFromEntryPoint() public { - UserOperation[] memory userOpAux = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()"), "" - ); - - vm.prank(paymasterAdmin); - vm.expectRevert("Sender not EntryPoint"); - openfortPaymaster.validatePaymasterUserOp(userOpAux[0], bytes32(""), 0); - - vm.prank(paymasterAdmin); - vm.expectRevert("Sender not EntryPoint"); - openfortPaymaster.postOp(IPaymaster.PostOpMode(0), bytes(""), 0); - } - - /* - * Test basic transfer ownership - */ - function testAcceptOwnershipBasic() public { - assertEq(openfortPaymaster.owner(), paymasterAdmin); - - vm.expectRevert("Ownable: caller is not the owner"); - openfortPaymaster.transferOwnership(factoryAdmin); - - vm.prank(paymasterAdmin); - openfortPaymaster.transferOwnership(factoryAdmin); - - vm.expectRevert("Ownable2Step: caller is not the new owner"); - openfortPaymaster.acceptOwnership(); - - vm.prank(factoryAdmin); - openfortPaymaster.acceptOwnership(); - assertEq(openfortPaymaster.owner(), factoryAdmin); - } - - /* - * ToDo Test complex transfer ownership - */ - function testAcceptOwnershipComplex() public { - assertEq(openfortPaymaster.owner(), paymasterAdmin); - - // Play around with deposits - assertEq(openfortPaymaster.getDeposit(), 50 ether); - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); - - openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); - - assertEq(openfortPaymaster.getDeposit(), 53 ether); - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); - - vm.prank(paymasterAdmin); - openfortPaymaster.transferOwnership(factoryAdmin); - - assertEq(openfortPaymaster.getDeposit(), 53 ether); - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); - // Play around with deposits - - vm.prank(factoryAdmin); - openfortPaymaster.acceptOwnership(); - assertEq(openfortPaymaster.owner(), factoryAdmin); - - // After transferring the ownership, the old owner does not have any deposit - // and the new one has all deposit from previous owner PLUS its old deposit - - assertEq(openfortPaymaster.getDeposit(), 53 ether); - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 0 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 53 ether); - } - - /* - * Test using a new depositor (factoryAdmin) - * Should work - */ - function testPaymasterUserOpNativeValidSigDEPOSITOR() public { - bytes memory dataEncoded = mockedPaymasterDataNative(factoryAdmin); - - assertEq(openfortPaymaster.getDeposit(), 50 ether); - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); - - openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); - - assertEq(openfortPaymaster.getDeposit(), 53 ether); - assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); - - bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - console.log("paymasterAndData"); - console.log("paymasterAndData"); - console.logBytes(paymasterAndData); - - UserOperation[] memory userOps = _setupUserOpExecute( - account, - accountAdminPKey, - bytes(""), - address(testCounter), - 0, - abi.encodeWithSignature("count()"), - paymasterAndData - ); - OpenfortPaymasterV2.PolicyStrategy memory strategy; - strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; - strategy.depositor = factoryAdmin; - strategy.erc20Token = address(0); - strategy.exchangeRate = 0; - bytes32 hash; - { - // Simulating that the Paymaster gets the userOp and signs it - hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); - bytes memory signature = abi.encodePacked(r, s, v); - bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); - userOps[0].paymasterAndData = paymasterAndDataSigned; - } - - console.log("userOps[0].paymasterAndData"); - console.log("userOps[0].paymasterAndData"); - console.logBytes(userOps[0].paymasterAndData); - - // Back to the user. Sign the userOp - bytes memory userOpSignature; - bytes32 hash2; - { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - userOpSignature = abi.encodePacked(r, s, v); - - // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - - // Should return account admin - hash2 = - ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - } - - // The hash of the userOp should not have changed after the inclusion of the sig - assertEq(hash, hash2); - userOps[0].signature = userOpSignature; - - // Get the paymaster deposit before handling the userOp - uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - - entryPoint.handleOps(userOps, beneficiary); - // entryPoint.simulateValidation(userOp); - - // Verify that the paymaster has less deposit now - assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); - //Verify that the counter has increased - assertEq(testCounter.counters(account), 1); - - assert(openfortPaymaster.getDeposit() < 53 ether); // less than 53 because the total cost have decreased - assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // more than 50 because the dust has gone to the owner deposit - assert(openfortPaymaster.getDepositFor(factoryAdmin) < 3 ether); // less than 3 because the gas cost was paid using factoryAdmin's deposit - } -} +// contract OpenfortPaymasterV2Test is Test { +// using ECDSA for bytes32; + +// EntryPoint public entryPoint; +// UpgradeableOpenfortAccount public staticOpenfortAccount; +// UpgradeableOpenfortFactory public staticOpenfortFactory; +// OpenfortPaymasterV2 public openfortPaymaster; +// address public account; +// TestCounter public testCounter; +// TestToken public testToken; + +// // Testing addresses +// address private factoryAdmin; +// uint256 private factoryAdminPKey; + +// address private paymasterAdmin; +// uint256 private paymasterAdminPKey; + +// address private accountAdmin; +// uint256 private accountAdminPKey; + +// address payable private beneficiary = payable(makeAddr("beneficiary")); + +// uint48 internal constant VALIDUNTIL = 2 ** 48 - 1; +// uint48 internal constant VALIDAFTER = 0; +// uint256 internal constant EXCHANGERATE = 10 ** 3; +// uint256 internal constant MOCKSIG = 2 ** 256 - 1; +// uint256 internal TESTTOKEN_ACCOUNT_PREFUND = 100 * 10 ** 18; + +// error InvalidTokenRecipient(); + +// event PostOpGasUpdated(uint256 oldPostOpGas, uint256 _newPostOpGas); + +// /* +// * Auxiliary function to generate a userOP +// */ +// function _setupUserOp( +// address sender, +// uint256 _signerPKey, +// bytes memory _initCode, +// bytes memory _callDataForEntrypoint, +// bytes memory paymasterAndData +// ) internal returns (UserOperation[] memory ops) { +// // Get user op fields +// UserOperation memory op = UserOperation({ +// sender: sender, +// nonce: entryPoint.getNonce(sender, 0), +// initCode: _initCode, +// callData: _callDataForEntrypoint, +// callGasLimit: 500_000, +// verificationGasLimit: 500_000, +// preVerificationGas: 500_000, +// maxFeePerGas: 1500000000, +// maxPriorityFeePerGas: 1500000000, +// paymasterAndData: paymasterAndData, +// signature: bytes("") +// }); + +// // Sign UserOp +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); +// bytes memory userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// // address recoveredSigner = ECDSA.recover(msgHash, v, r, s); +// // address expectedSigner = vm.addr(_signerPKey); +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(_signerPKey)); + +// op.signature = userOpSignature; + +// // Store UserOp +// ops = new UserOperation[](1); +// ops[0] = op; +// } + +// /* +// * Auxiliary function to generate a userOP using the execute() +// * from the account +// */ +// function _setupUserOpExecute( +// address sender, +// uint256 _signerPKey, +// bytes memory _initCode, +// address _target, +// uint256 _value, +// bytes memory _callData, +// bytes memory paymasterAndData +// ) internal returns (UserOperation[] memory) { +// bytes memory callDataForEntrypoint = +// abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); + +// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); +// } + +// /* +// * Auxiliary function to generate a userOP using the executeBatch() +// * from the account +// */ +// function _setupUserOpExecuteBatch( +// address sender, +// uint256 _signerPKey, +// bytes memory _initCode, +// address[] memory _target, +// uint256[] memory _value, +// bytes[] memory _callData, +// bytes memory paymasterAndData +// ) internal returns (UserOperation[] memory) { +// bytes memory callDataForEntrypoint = +// abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); + +// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); +// } + +// function mockedPaymasterDataNative(address _depositor) internal pure returns (bytes memory dataEncoded) { +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; +// strategy.depositor = _depositor; +// strategy.erc20Token = address(0); +// strategy.exchangeRate = 0; +// // Looking at the source code, I've found this part was not Packed (filled with 0s) +// dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); +// } + +// function mockedPaymasterDataERC20Dynamic(address _depositor) internal view returns (bytes memory dataEncoded) { +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; +// strategy.depositor = _depositor; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = EXCHANGERATE; +// // Looking at the source code, I've found this part was not Packed (filled with 0s) +// dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); +// } + +// function mockedPaymasterDataERC20Fixed(address _depositor, uint256 _pricePerTransaction) +// internal +// view +// returns (bytes memory dataEncoded) +// { +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; +// strategy.depositor = _depositor; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = _pricePerTransaction; +// // Looking at the source code, I've found this part was not Packed (filled with 0s) +// dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); +// } + +// /** +// * @notice Initialize the UpgradeableOpenfortAccount testing contract. +// * Scenario: +// * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory +// * - paymasterAdmin is the deployer (and owner) of the OpenfortPaymaster +// * - accountAdmin is the account used to deploy new static accounts +// * - entryPoint is the singleton EntryPoint +// * - testCounter is the counter used to test userOps +// */ +// function setUp() public { +// // Setup and fund signers +// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); +// vm.deal(factoryAdmin, 100 ether); +// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); +// vm.deal(accountAdmin, 100 ether); +// (paymasterAdmin, paymasterAdminPKey) = makeAddrAndKey("paymasterAdmin"); +// vm.deal(paymasterAdmin, 100 ether); + +// // If we are in a fork +// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { +// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); +// } +// // If not a fork, deploy entryPoint (at correct address) +// else { +// EntryPoint entryPoint_aux = new EntryPoint(); +// bytes memory code = address(entryPoint_aux).code; +// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); +// vm.etch(targetAddr, code); +// entryPoint = EntryPoint(payable(targetAddr)); +// } +// vm.prank(paymasterAdmin); +// openfortPaymaster = new OpenfortPaymasterV2(IEntryPoint(payable(address(entryPoint))), paymasterAdmin); +// // Paymaster deposits 50 ETH to EntryPoint +// vm.prank(paymasterAdmin); +// openfortPaymaster.deposit{value: 50 ether}(); +// // Paymaster stakes 25 ETH +// vm.prank(paymasterAdmin); +// openfortPaymaster.addStake{value: 25 ether}(1); + +// // deploy account factory +// vm.prank(factoryAdmin); +// staticOpenfortAccount = new UpgradeableOpenfortAccount(); +// vm.prank(factoryAdmin); +// staticOpenfortFactory = +// new UpgradeableOpenfortFactory((payable(vm.envAddress("ENTRY_POINT_ADDRESS"))), address(staticOpenfortAccount)); +// // deploy a new TestCounter +// testCounter = new TestCounter(); +// // deploy a new TestToken (ERC20) and mint 1000 +// testToken = new TestToken(); +// testToken.mint(address(this), 1_000 * 10 ** 18); + +// // Create an static account wallet and get its address +// vm.prank(factoryAdmin); +// account = staticOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); +// } + +// /* +// * Test initial parameters +// * +// */ +// function testInitialParameters() public { +// assertEq(address(openfortPaymaster.entryPoint()), vm.envAddress("ENTRY_POINT_ADDRESS")); +// assertEq(address(openfortPaymaster.owner()), paymasterAdmin); +// } + +// /** +// * Deposit should fail if not the owner +// */ +// function testFailDeposit() public { +// vm.prank(factoryAdmin); +// openfortPaymaster.deposit{value: 50 ether}(); +// } + +// /* +// * Test parsePaymasterAndData() when using the native token +// * +// */ +// function testParsePaymasterDataNative() public { +// // Encode the paymaster data +// bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); + +// // Get the related paymaster data signature +// bytes32 hash = keccak256(dataEncoded); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); + +// // Create the paymasterAndData info +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) + +// ( +// uint48 returnedValidUntil, +// uint48 returnedValidAfter, +// OpenfortPaymasterV2.PolicyStrategy memory strategy, +// bytes memory returnedSignature +// ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); + +// assertEq(returnedValidUntil, VALIDUNTIL); +// assertEq(returnedValidAfter, VALIDAFTER); +// assertEq(strategy.erc20Token, address(0)); +// assertEq(strategy.exchangeRate, 0); +// assertEq(signature, returnedSignature); +// } + +// /* +// * Test parsePaymasterAndData() with an ERC20 dynamic +// * +// */ +// function testParsePaymasterDataERC20() public { +// // Encode the paymaster data +// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); + +// // Get the related paymaster data signature +// bytes32 hash = keccak256(dataEncoded); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); + +// // Create the paymasterAndData info +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// console.logBytes(paymasterAndData); + +// ( +// uint48 returnedValidUntil, +// uint48 returnedValidAfter, +// OpenfortPaymasterV2.PolicyStrategy memory strategy, +// bytes memory returnedSignature +// ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); +// assertEq(returnedValidUntil, VALIDUNTIL); +// assertEq(returnedValidAfter, VALIDAFTER); +// assertEq(strategy.erc20Token, address(testToken)); +// assertEq(strategy.exchangeRate, EXCHANGERATE); +// assertEq(signature, returnedSignature); +// } + +// /* +// * The owner (paymasterAdmin) can add and withdraw stake. +// * Others cannot. +// */ +// function testPaymasterStake() public { +// assertEq(paymasterAdmin.balance, 25 ether); + +// // The owner can add stake +// vm.prank(paymasterAdmin); +// openfortPaymaster.addStake{value: 2 ether}(10); +// assertEq(paymasterAdmin.balance, 23 ether); + +// // Others cannot add stake +// vm.expectRevert("Ownable: caller is not the owner"); +// openfortPaymaster.addStake{value: 2}(10); + +// // The owner trying to withdraw stake fails because it has not unlocked +// // The owner can withdraw stake +// vm.prank(paymasterAdmin); +// vm.expectRevert(); +// openfortPaymaster.withdrawStake(payable(paymasterAdmin)); + +// // The owner unlocks the stake +// vm.prank(paymasterAdmin); +// openfortPaymaster.unlockStake(); + +// // The owner trying to unlock fails because it has not passed enought time +// vm.prank(paymasterAdmin); +// vm.expectRevert(); +// openfortPaymaster.withdrawStake(payable(paymasterAdmin)); + +// // Passes 20 blocks... +// skip(20); + +// // The owner can now withdraw stake (the 2 ethers recently staked + the 25 from the SetUp) +// vm.prank(paymasterAdmin); +// openfortPaymaster.withdrawStake(payable(paymasterAdmin)); +// assertEq(paymasterAdmin.balance, 50 ether); +// } + +// /* +// * Complete deposit walkthrough test +// */ +// function testDepositsToPaymaster() public { +// // Initially, the Paymaster has 50 ether deposited +// assertEq(entryPoint.balanceOf(address(openfortPaymaster)), 50 ether); + +// // Directly deposit 1 ETH to EntryPoint on behalf of the Paymaster +// entryPoint.depositTo{value: 1 ether}(address(openfortPaymaster)); +// assertEq(entryPoint.balanceOf(address(openfortPaymaster)), 51 ether); + +// // Cannot deposit to address 0 +// vm.expectRevert(); +// openfortPaymaster.depositFor{value: 0 ether}(address(0)); + +// // Cannot deposit 0 ether +// vm.expectRevert(); +// openfortPaymaster.depositFor{value: 0 ether}(factoryAdmin); + +// // Cannot depositFor using owner +// vm.prank(paymasterAdmin); +// vm.expectRevert(); +// openfortPaymaster.depositFor{value: 1 ether}(paymasterAdmin); + +// // Paymaster deposits 1 ETH to EntryPoint +// openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + +// // Get the WHOLE deposited amount so far +// assertEq(openfortPaymaster.getDeposit(), 52 ether); +// // Notice that, even though the deposit was made to the EntryPoint for the openfortPaymaster, the deposit is 0: +// assertEq(openfortPaymaster.getDepositFor(address(openfortPaymaster)), 0 ether); + +// // All deposit not made using "depositFor" goes to owner (paymasterAdmin) +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 51 ether); + +// vm.expectRevert("Not Owner: use depositFor() instead"); +// openfortPaymaster.deposit{value: 1 ether}(); +// // Get the WHOLE deposited amount so far +// assertEq(openfortPaymaster.getDeposit(), 52 ether); + +// vm.prank(paymasterAdmin); +// openfortPaymaster.deposit{value: 1 ether}(); +// // Get the WHOLE deposited amount so far +// assertEq(openfortPaymaster.getDeposit(), 53 ether); + +// vm.expectRevert(); +// openfortPaymaster.withdrawTo(payable(paymasterAdmin), 1 ether); +// assertEq(openfortPaymaster.getDeposit(), 53 ether); + +// vm.prank(paymasterAdmin); +// openfortPaymaster.withdrawTo(payable(paymasterAdmin), 1 ether); +// // Get the WHOLE deposited amount so far +// assertEq(openfortPaymaster.getDeposit(), 52 ether); + +// vm.expectRevert(); +// vm.prank(paymasterAdmin); +// openfortPaymaster.withdrawTo(payable(paymasterAdmin), 10000 ether); +// // Get the WHOLE deposited amount so far +// assertEq(openfortPaymaster.getDeposit(), 52 ether); + +// // Let's now use withdrawDepositorTo +// // Owner cannot call it +// vm.expectRevert(); +// vm.prank(paymasterAdmin); +// openfortPaymaster.withdrawDepositorTo(payable(paymasterAdmin), 1 ether); + +// // factoryAdmin can call it +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); +// assertEq(factoryAdmin.balance, 100 ether); +// // but not too much! +// vm.expectRevert(); +// vm.prank(factoryAdmin); +// openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1000 ether); +// // not using address 0! +// vm.expectRevert(); +// vm.prank(factoryAdmin); +// openfortPaymaster.withdrawDepositorTo(payable(address(0)), 1 ether); +// vm.prank(factoryAdmin); +// openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); +// assertEq(factoryAdmin.balance, 101 ether); +// // Deposit of the owner is still 51 +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 51 ether); + +// // Finally, let's test withdrawFromDepositor +// // deposit again using factoryAdmin +// openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); +// // Only owner cannot call it +// vm.expectRevert(); +// openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + +// vm.expectRevert(); +// vm.prank(paymasterAdmin); +// openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 100 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + +// vm.prank(paymasterAdmin); +// openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); +// } + +// /* +// * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig length) +// * Should revert +// */ +// function testPaymasterUserOpWrongSigLength() public { +// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, "0x1234"); // This part was packed (not filled with 0s) + +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testCounter), +// 0, +// abi.encodeWithSignature("count()"), +// paymasterAndData +// ); + +// // "ECDSA: invalid signature length" +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); +// } + +// /* +// * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig) +// * Should revert +// */ +// function testPaymasterUserOpWrongSig() public { +// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); // MOCKSIG, "1", MOCKSIG to make sure we send 65 bytes as sig +// UserOperation[] memory userOp = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testCounter), +// 0, +// abi.encodeWithSignature("count()"), +// paymasterAndData +// ); + +// // "AA33 reverted: ECDSA: invalid signature" +// vm.expectRevert(); +// entryPoint.simulateValidation(userOp[0]); + +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); +// } + +// /* +// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) +// * Should work +// */ +// function testPaymasterUserOpNativeValidSig() public { +// bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// UserOperation[] memory userOps = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testCounter), +// 0, +// abi.encodeWithSignature("count()"), +// paymasterAndData +// ); +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(0); +// strategy.exchangeRate = 0; +// bytes32 hash; +// { +// // Simulating that the Paymaster gets the userOp and signs it +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); +// // entryPoint.simulateValidation(userOp); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// //Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Test sending a userOp with signature from a wrong address +// * Should not work +// */ +// function testPaymasterUserOpNativeWrongUserSig() public { +// bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// UserOperation[] memory userOps = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testCounter), +// 0, +// abi.encodeWithSignature("count()"), +// paymasterAndData +// ); +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; +// strategy.erc20Token = address(0); +// strategy.exchangeRate = EXCHANGERATE; +// bytes32 hash; +// { +// // Simulating that the factory admin gets the userOp and tries to sign it +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(factoryAdmin, ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(factoryAdminPKey)); + +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// vm.expectRevert(); +// entryPoint.handleOps(userOps, beneficiary); +// // entryPoint.simulateValidation(userOp); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); +// //Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); +// } + +// /* +// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) +// * Using ERC20. Should work +// */ +// function testPaymasterUserOpERC20ValidSigDiffMaxPriorityFeePerGas() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + +// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testToken), +// 0, +// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), +// paymasterAndData +// ); + +// userOps[0].maxPriorityFeePerGas += 1; +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = EXCHANGERATE; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has decreased +// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); +// } + +// /* +// * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) +// * Using ERC20. Should work +// */ +// function testPaymasterUserOpERC20ValidSig() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + +// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testToken), +// 0, +// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), +// paymasterAndData +// ); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = EXCHANGERATE; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has decreased +// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); +// } + +// /* +// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) +// * Using FIXED ERC20. Should work +// */ +// function testPaymasterUserOpERC20FixedValidSig() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); +// uint256 pricePerTransaction = 10 ** 18; + +// bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testToken), +// 0, +// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), +// paymasterAndData +// ); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = pricePerTransaction; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has decreased +// assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); +// } + +// /* +// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) +// * ExecBatch. Using dynamic ERC20. Should work +// */ +// function testPaymasterUserOpERC20ValidSigExecBatch() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + +// assertEq(testCounter.counters(account), 0); + +// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// uint256 count = 2; +// address[] memory targets = new address[](count); +// uint256[] memory values = new uint256[](count); +// bytes[] memory callData = new bytes[](count); + +// targets[0] = address(testToken); +// values[0] = 0; +// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + +// targets[1] = address(testCounter); +// values[1] = 0; +// callData[1] = abi.encodeWithSignature("count()"); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = +// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = EXCHANGERATE; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has decreased +// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) +// * ExecBatch. Using fixed ERC20. Should work +// */ +// function testPaymasterUserOpERC20FixedValidSigExecBatch() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + +// assertEq(testCounter.counters(account), 0); + +// uint256 pricePerTransaction = 10 ** 18; + +// bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// uint256 count = 2; +// address[] memory targets = new address[](count); +// uint256[] memory values = new uint256[](count); +// bytes[] memory callData = new bytes[](count); + +// targets[0] = address(testToken); +// values[0] = 0; +// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + +// targets[1] = address(testCounter); +// values[1] = 0; +// callData[1] = abi.encodeWithSignature("count()"); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = +// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = pricePerTransaction; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has decreased +// assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) +// * ExecBatch. Using fixed ERC20 expensive. Should work +// */ +// function testPaymasterUserOpERC20FixedExpensiveValidSigExecBatch() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + +// assertEq(testCounter.counters(account), 0); + +// uint256 pricePerTransaction = 10; + +// bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// uint256 count = 2; +// address[] memory targets = new address[](count); +// uint256[] memory values = new uint256[](count); +// bytes[] memory callData = new bytes[](count); + +// targets[0] = address(testToken); +// values[0] = 0; +// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + +// targets[1] = address(testCounter); +// values[1] = 0; +// callData[1] = abi.encodeWithSignature("count()"); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = +// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = pricePerTransaction; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has decreased +// assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) +// * ExecBatch. Should work +// */ +// function testPaymasterUserOpNativeValidSigExecBatch() public { +// bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// uint256 count = 2; +// address[] memory targets = new address[](count); +// uint256[] memory values = new uint256[](count); +// bytes[] memory callData = new bytes[](count); + +// targets[0] = address(testToken); +// values[0] = 0; +// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + +// targets[1] = address(testCounter); +// values[1] = 0; +// callData[1] = abi.encodeWithSignature("count()"); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = +// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(0); +// strategy.exchangeRate = 0; + +// bytes32 hash; +// { +// // Simulating that the Paymaster gets the userOp and signs it +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); +// // entryPoint.simulateValidation(userOp); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// //Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); +// } + +// /* +// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) +// * ExecBatch. Using ERC20. Should work. +// * Test showing that failing to repay in ERC20 still spends some of Paymaster's deposit (DoS) +// */ +// function testFailPaymasterUserOpERC20ValidSigExecBatchInsufficientERC20() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, 100); +// assertEq(testToken.balanceOf(account), 100); + +// assertEq(testCounter.counters(account), 0); + +// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// uint256 count = 2; +// address[] memory targets = new address[](count); +// uint256[] memory values = new uint256[](count); +// bytes[] memory callData = new bytes[](count); + +// targets[0] = address(testToken); +// values[0] = 0; +// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + +// targets[1] = address(testCounter); +// values[1] = 0; +// callData[1] = abi.encodeWithSignature("count()"); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = +// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = EXCHANGERATE; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has the same deposit +// assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has not decreased +// assertEq(testToken.balanceOf(account), 100); +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); + +// // If this fails, it would mean: +// // 1- That the paymaster has spent some of its deposit +// // 2- That the smart account could not perform the desired actions, but still has all testTokens +// // An attacker could DoS the paymaster to drain its deposit +// } + +// /* +// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) +// * Using ERC20. Should work. +// */ +// function testFailPaymasterUserOpERC20ValidSigSmallApprove() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + +// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testToken), +// 0, +// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 1), +// paymasterAndData +// ); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; +// strategy.depositor = paymasterAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = EXCHANGERATE; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has the same deposit +// assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has not decreased +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); +// // Verify that the counter has not increased +// assertEq(testCounter.counters(account), 0); + +// // If this fails, it would mean: +// // 1- That the paymaster has spent some of its deposit +// // 2- That the smart account could not perform the desired actions, but still has all testTokens +// // An attacker could DoS the paymaster to drain its deposit +// } + +// /* +// * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) +// * Using ERC20 and a 3rd party depositor. Should work +// */ +// function testPaymasterUserOpERC20ValidSigDepositor() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + +// vm.prank(factoryAdmin); +// openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); +// assertEq(openfortPaymaster.getDeposit(), 100 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); + +// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(factoryAdmin); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testToken), +// 0, +// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), +// paymasterAndData +// ); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; +// strategy.depositor = factoryAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = EXCHANGERATE; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has decreased +// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + +// assert(openfortPaymaster.getDeposit() < 100 ether); +// assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); +// assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // deposit of the owner should have increased a bit because of the dust +// } + +// /* +// * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) +// * Using ERC20 (fixed rate) and a 3rd party depositor. Should work +// */ +// function testPaymasterUserOpERC20FixedValidSigDepositor() public { +// assertEq(testToken.balanceOf(account), 0); +// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); +// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + +// vm.prank(factoryAdmin); +// openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); +// assertEq(openfortPaymaster.getDeposit(), 100 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); + +// uint256 pricePerTransaction = 10 ** 18; + +// bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(factoryAdmin, pricePerTransaction); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + +// // Create a userOp to let the paymaster use our testTokens +// UserOperation[] memory userOps = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testToken), +// 0, +// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), +// paymasterAndData +// ); + +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; +// strategy.depositor = factoryAdmin; +// strategy.erc20Token = address(testToken); +// strategy.exchangeRate = pricePerTransaction; + +// // Simulating that the Paymaster gets the userOp and signs it +// bytes32 hash; +// { +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// // Verify that the balance of the smart account has decreased +// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + +// assert(openfortPaymaster.getDeposit() < 100 ether); +// assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); +// assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // deposit of the owner should have increased a bit because of the dust +// } + +// /* +// * Test setPostOpGas function +// */ +// function testSetPostOpGas() public { +// vm.expectRevert("Ownable: caller is not the owner"); +// openfortPaymaster.setPostOpGas(15_000); + +// vm.prank(paymasterAdmin); +// vm.expectRevert(); +// openfortPaymaster.setPostOpGas(0); + +// // Expect that we will see a PostOpGasUpdated event +// vm.prank(paymasterAdmin); +// vm.expectEmit(true, true, false, false); +// emit PostOpGasUpdated(40_000, 15_000); +// openfortPaymaster.setPostOpGas(15_000); +// } + +// /* +// * Trigger _requireFromEntryPoint() from BaseOpenfortPaymaster +// */ +// function test_requireFromEntryPoint() public { +// UserOperation[] memory userOpAux = _setupUserOpExecute( +// account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()"), "" +// ); + +// vm.prank(paymasterAdmin); +// vm.expectRevert("Sender not EntryPoint"); +// openfortPaymaster.validatePaymasterUserOp(userOpAux[0], bytes32(""), 0); + +// vm.prank(paymasterAdmin); +// vm.expectRevert("Sender not EntryPoint"); +// openfortPaymaster.postOp(IPaymaster.PostOpMode(0), bytes(""), 0); +// } + +// /* +// * Test basic transfer ownership +// */ +// function testAcceptOwnershipBasic() public { +// assertEq(openfortPaymaster.owner(), paymasterAdmin); + +// vm.expectRevert("Ownable: caller is not the owner"); +// openfortPaymaster.transferOwnership(factoryAdmin); + +// vm.prank(paymasterAdmin); +// openfortPaymaster.transferOwnership(factoryAdmin); + +// vm.expectRevert("Ownable2Step: caller is not the new owner"); +// openfortPaymaster.acceptOwnership(); + +// vm.prank(factoryAdmin); +// openfortPaymaster.acceptOwnership(); +// assertEq(openfortPaymaster.owner(), factoryAdmin); +// } + +// /* +// * ToDo Test complex transfer ownership +// */ +// function testAcceptOwnershipComplex() public { +// assertEq(openfortPaymaster.owner(), paymasterAdmin); + +// // Play around with deposits +// assertEq(openfortPaymaster.getDeposit(), 50 ether); +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); + +// openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); + +// assertEq(openfortPaymaster.getDeposit(), 53 ether); +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); + +// vm.prank(paymasterAdmin); +// openfortPaymaster.transferOwnership(factoryAdmin); + +// assertEq(openfortPaymaster.getDeposit(), 53 ether); +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); +// // Play around with deposits + +// vm.prank(factoryAdmin); +// openfortPaymaster.acceptOwnership(); +// assertEq(openfortPaymaster.owner(), factoryAdmin); + +// // After transferring the ownership, the old owner does not have any deposit +// // and the new one has all deposit from previous owner PLUS its old deposit + +// assertEq(openfortPaymaster.getDeposit(), 53 ether); +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 0 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 53 ether); +// } + +// /* +// * Test using a new depositor (factoryAdmin) +// * Should work +// */ +// function testPaymasterUserOpNativeValidSigDEPOSITOR() public { +// bytes memory dataEncoded = mockedPaymasterDataNative(factoryAdmin); + +// assertEq(openfortPaymaster.getDeposit(), 50 ether); +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); + +// openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); + +// assertEq(openfortPaymaster.getDeposit(), 53 ether); +// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); +// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); + +// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); +// console.log("paymasterAndData"); +// console.log("paymasterAndData"); +// console.logBytes(paymasterAndData); + +// UserOperation[] memory userOps = _setupUserOpExecute( +// account, +// accountAdminPKey, +// bytes(""), +// address(testCounter), +// 0, +// abi.encodeWithSignature("count()"), +// paymasterAndData +// ); +// OpenfortPaymasterV2.PolicyStrategy memory strategy; +// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; +// strategy.depositor = factoryAdmin; +// strategy.erc20Token = address(0); +// strategy.exchangeRate = 0; +// bytes32 hash; +// { +// // Simulating that the Paymaster gets the userOp and signs it +// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); +// bytes memory signature = abi.encodePacked(r, s, v); +// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) +// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); +// userOps[0].paymasterAndData = paymasterAndDataSigned; +// } + +// console.log("userOps[0].paymasterAndData"); +// console.log("userOps[0].paymasterAndData"); +// console.logBytes(userOps[0].paymasterAndData); + +// // Back to the user. Sign the userOp +// bytes memory userOpSignature; +// bytes32 hash2; +// { +// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); +// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + +// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); +// userOpSignature = abi.encodePacked(r, s, v); + +// // Verifications below commented to avoid "Stack too deep" error +// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + +// // Should return account admin +// hash2 = +// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); +// } + +// // The hash of the userOp should not have changed after the inclusion of the sig +// assertEq(hash, hash2); +// userOps[0].signature = userOpSignature; + +// // Get the paymaster deposit before handling the userOp +// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + +// entryPoint.handleOps(userOps, beneficiary); +// // entryPoint.simulateValidation(userOp); + +// // Verify that the paymaster has less deposit now +// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); +// //Verify that the counter has increased +// assertEq(testCounter.counters(account), 1); + +// assert(openfortPaymaster.getDeposit() < 53 ether); // less than 53 because the total cost have decreased +// assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // more than 50 because the dust has gone to the owner deposit +// assert(openfortPaymaster.getDepositFor(factoryAdmin) < 3 ether); // less than 3 because the gas cost was paid using factoryAdmin's deposit +// } +// } From e3ed9430500b976b4b136642d4fac7dc25efc424 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 21 Nov 2023 12:26:38 +0100 Subject: [PATCH 27/83] More refactoring --- contracts/core/base/BaseOpenfortAccount.sol | 13 +- .../core/base/BaseRecoverableAccount.sol | 5 +- .../core/eip6551/EIP6551OpenfortAccount.sol | 2 - .../interfaces/OpenfortErrorsAndEvents.sol | 10 + ...estCounter.sol => UserOpTestCounter.s.sol} | 0 script/debugUserOp.s.sol | 4 +- ...{deployEIP6551.sol => deployEIP6551.s.sol} | 0 ...counts.sol => deployManagedAccounts.s.sol} | 0 script/{deployMock.sol => deployMock.s.sol} | 0 script/deployRecoverableAccounts.sol | 39 - ...ts.sol => deployUpgradeableAccounts.s.sol} | 0 script/{signMessage.sol => signMessage.s.sol} | 0 test/foundry/4337/Specific4337Tests.t.sol | 220 -- .../eip6551/EIP6551OpenfortAccountTest.t.sol | 105 +- .../eip6551/EIP6551OpenfortBenchmark.t.sol | 1 - .../paymaster/OpenfortPaymasterV2Test.t.sol | 3368 ++++++++--------- test/foundry/utils/SigUtils.sol | 13 - 17 files changed, 1708 insertions(+), 2072 deletions(-) rename script/{UserOpTestCounter.sol => UserOpTestCounter.s.sol} (100%) rename script/{deployEIP6551.sol => deployEIP6551.s.sol} (100%) rename script/{deployManagedAccounts.sol => deployManagedAccounts.s.sol} (100%) rename script/{deployMock.sol => deployMock.s.sol} (100%) delete mode 100644 script/deployRecoverableAccounts.sol rename script/{deployUpgradeableAccounts.sol => deployUpgradeableAccounts.s.sol} (100%) rename script/{signMessage.sol => signMessage.s.sol} (100%) delete mode 100644 test/foundry/4337/Specific4337Tests.t.sol delete mode 100644 test/foundry/utils/SigUtils.sol diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 39e9e7a..05b4677 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -9,6 +9,7 @@ import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/mat import {BaseAccount, UserOperation, IEntryPoint, UserOperationLib} from "account-abstraction/core/BaseAccount.sol"; import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; import {_packValidationData} from "account-abstraction/core/Helpers.sol"; +import {OpenfortErrorsAndEvents} from "../../interfaces/OpenfortErrorsAndEvents.sol"; /** * @title BaseOpenfortAccount (Non upgradeable by default) @@ -25,7 +26,8 @@ abstract contract BaseOpenfortAccount is Initializable, EIP712Upgradeable, IERC1271Upgradeable, - TokenCallbackHandler + TokenCallbackHandler, + OpenfortErrorsAndEvents { using ECDSAUpgradeable for bytes32; @@ -59,15 +61,6 @@ abstract contract BaseOpenfortAccount is mapping(address sessionKey => SessionKeyStruct sessionKeyData) public sessionKeys; - event AccountImplementationDeployed(address indexed creator); - event SessionKeyRegistered(address indexed key); - event SessionKeyRevoked(address indexed key); - - error ZeroAddressNotAllowed(); - error NotOwnerOrEntrypoint(); - error NotOwner(); - error InvalidParameterLength(); - receive() external payable virtual {} constructor() { diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index bab0ca2..2e72a03 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -53,7 +53,6 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg // keccak256("Recover(address recoveryAddress,uint64 executeAfter,uint32 guardiansRequired)"); bytes32 private constant RECOVER_TYPEHASH = 0x9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5; - event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); event Locked(bool isLocked); event GuardianProposed(address indexed guardian, uint256 executeAfter); event GuardianAdded(address indexed guardian); @@ -63,7 +62,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg event GuardianRevocationCancelled(address indexed guardian); event RecoveryExecuted(address indexed recoveryAddress, uint64 executeAfter); event RecoveryCompleted(address indexed recoveryAddress); - event RecoveryCanceled(address indexed recoveryAddress); + event RecoveryCancelled(address indexed recoveryAddress); error AccountLocked(); error AccountNotLocked(); @@ -410,7 +409,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg function cancelRecovery() external onlyOwner { _requireRecovery(true); address recoveryOwner = recoveryDetails.recoveryAddress; - emit RecoveryCanceled(recoveryOwner); + emit RecoveryCancelled(recoveryOwner); delete recoveryDetails; _setLock(0); } diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/eip6551/EIP6551OpenfortAccount.sol index 45ea319..ebe0d07 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/eip6551/EIP6551OpenfortAccount.sol @@ -30,8 +30,6 @@ contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 error OperationNotAllowed(); - event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); - receive() external payable override(BaseOpenfortAccount, IERC6551Account) {} /* diff --git a/contracts/interfaces/OpenfortErrorsAndEvents.sol b/contracts/interfaces/OpenfortErrorsAndEvents.sol index 4554381..fada2cf 100644 --- a/contracts/interfaces/OpenfortErrorsAndEvents.sol +++ b/contracts/interfaces/OpenfortErrorsAndEvents.sol @@ -11,6 +11,16 @@ interface OpenfortErrorsAndEvents { /// @notice Error when a function requires msg.value to be different than owner() error OwnerNotAllowed(); + error ZeroAddressNotAllowed(); + error NotOwnerOrEntrypoint(); + error NotOwner(); + error InvalidParameterLength(); + + event AccountImplementationDeployed(address indexed creator); + event SessionKeyRegistered(address indexed key); + event SessionKeyRevoked(address indexed key); + event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); + // Paymaster specifics /** diff --git a/script/UserOpTestCounter.sol b/script/UserOpTestCounter.s.sol similarity index 100% rename from script/UserOpTestCounter.sol rename to script/UserOpTestCounter.s.sol diff --git a/script/debugUserOp.s.sol b/script/debugUserOp.s.sol index 33372d6..5f06d24 100644 --- a/script/debugUserOp.s.sol +++ b/script/debugUserOp.s.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import "forge-std/Script.sol"; +import {Script} from "forge-std/Script.sol"; import {UserOperation, EntryPoint} from "account-abstraction/core/EntryPoint.sol"; contract DebugUserOp is Script { @@ -26,6 +26,6 @@ contract DebugUserOp is Script { entryPoint.simulateHandleOp(userOp, address(0), ""); // simulateHandleOp() will always return the following error: - // error ExecutionResult(uint256 preOpGas, uint256 paid, uint48 validAfter, uint48 validUntil, bool targetSuccess, bytes targetResult); + // error ExecutionResult(uint256 preOpGas, uint256 paid, uint48 validAfter, uint48 validUntil, bool targetSuccess, bytes targetResult); } } diff --git a/script/deployEIP6551.sol b/script/deployEIP6551.s.sol similarity index 100% rename from script/deployEIP6551.sol rename to script/deployEIP6551.s.sol diff --git a/script/deployManagedAccounts.sol b/script/deployManagedAccounts.s.sol similarity index 100% rename from script/deployManagedAccounts.sol rename to script/deployManagedAccounts.s.sol diff --git a/script/deployMock.sol b/script/deployMock.s.sol similarity index 100% rename from script/deployMock.sol rename to script/deployMock.s.sol diff --git a/script/deployRecoverableAccounts.sol b/script/deployRecoverableAccounts.sol deleted file mode 100644 index e841a12..0000000 --- a/script/deployRecoverableAccounts.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -// import {Script, console} from "forge-std/Script.sol"; -// import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; -// import {RecoverableOpenfortAccount} from "../contracts/core/recoverable/RecoverableOpenfortAccount.sol"; -// import {RecoverableOpenfortFactory} from "../contracts/core/recoverable/RecoverableOpenfortFactory.sol"; - -// contract RecoverableOpenfortDeploy is Script { -// uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); -// // uint256 internal deployPrivKey = vm.envUint("PK"); -// address internal deployAddress = vm.addr(deployPrivKey); -// IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - -// uint256 private constant RECOVERY_PERIOD = 2 days; -// uint256 private constant SECURITY_PERIOD = 1.5 days; -// uint256 private constant SECURITY_WINDOW = 0.5 days; -// uint256 private constant LOCK_PERIOD = 5 days; -// address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_TESTNET"); - -// function run() public { -// bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); -// vm.startBroadcast(deployPrivKey); - -// RecoverableOpenfortAccount recoverableOpenfortAccountImpl = new RecoverableOpenfortAccount{salt: versionSalt}(); - -// RecoverableOpenfortFactory recoverableOpenfortFactory = -// new RecoverableOpenfortFactory{salt: versionSalt}(address(entryPoint), address(recoverableOpenfortAccountImpl), RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, OPENFORT_GUARDIAN); -// // (upgradeableOpenfortFactory); -// // address account1 = upgradeableOpenfortFactory.accountImplementation(); -// // The first call should create a new account, while the second will just return the corresponding account address -// address account2 = recoverableOpenfortFactory.createAccountWithNonce(deployAddress, "1"); -// console.log( -// "Factory at address %s has created an account at address %s", address(recoverableOpenfortFactory), account2 -// ); - -// vm.stopBroadcast(); -// } -// } diff --git a/script/deployUpgradeableAccounts.sol b/script/deployUpgradeableAccounts.s.sol similarity index 100% rename from script/deployUpgradeableAccounts.sol rename to script/deployUpgradeableAccounts.s.sol diff --git a/script/signMessage.sol b/script/signMessage.s.sol similarity index 100% rename from script/signMessage.sol rename to script/signMessage.s.sol diff --git a/test/foundry/4337/Specific4337Tests.t.sol b/test/foundry/4337/Specific4337Tests.t.sol deleted file mode 100644 index e2028d3..0000000 --- a/test/foundry/4337/Specific4337Tests.t.sol +++ /dev/null @@ -1,220 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -import {Test, console} from "lib/forge-std/src/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {TestToken} from "account-abstraction/test/TestToken.sol"; -import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; -import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; -import "account-abstraction/core/Helpers.sol" as Helpers; - -contract Specific4337Tests is Test { - using ECDSA for bytes32; - - uint48 constant MAX_TIME = 2 ** 48 - 1; - - EntryPoint public entryPoint; - UpgradeableOpenfortFactory public staticOpenfortFactory; - TestCounter public testCounter; - TestToken public testToken; - - // Testing addresses - address private factoryAdmin; - uint256 private factoryAdminPKey; - - address private accountAdmin; - uint256 private accountAdminPKey; - - address payable private beneficiary = payable(makeAddr("beneficiary")); - - event AccountCreated(address indexed account, address indexed accountAdmin); - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (UserOperation[] memory ops) { - uint256 nonce = entryPoint.getNonce(sender, 0); - - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 0, - maxPriorityFeePerGas: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - function _getValidationData( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (uint256 validationData) { - UserOperation[] memory userOp = _setupUserOpExecute(sender, _signerPKey, _initCode, _target, _value, _callData); - - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOp[0]); - - vm.prank(address(entryPoint)); - return UpgradeableOpenfortAccount(payable(sender)).validateUserOp(userOp[0], opHash, 0); - } - - /** - * @notice Initialize the UpgradeableOpenfortAccount testing contract. - * Scenario: - * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory - * - accountAdmin is the account used to deploy new static accounts - * - entryPoint is the singleton EntryPoint - * - testCounter is the counter used to test userOps - */ - function setUp() public { - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint - else { - entryPoint = new EntryPoint(); - } - // deploy account factory - - // deploy a new TestCounter - testCounter = new TestCounter(); - // deploy a new TestToken (ERC20) - testToken = new TestToken(); - } - - // /* - // * Should succeed. Return 0 as it is the owner calling - // * - // */ - // function testValidateUserOp() public { - // // Create an static account wallet and get its address - // address account = staticOpenfortFactory.createAccount(accountAdmin, ""); - - // uint256 validationData = _getValidationData( - // account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - // ); - // assertEq(validationData, 0); - // } - - // /* - // * Should return 1, therefore fail - // * Use an incorrect private key - // */ - // function testWrongValidateUserOp() public { - // // Create an static account wallet and get its address - // address account = staticOpenfortFactory.createAccount(accountAdmin, ""); - - // // Using an invalid private key - // uint256 validationData = _getValidationData( - // account, factoryAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - // ); - - // assertEq(validationData, 1); - // } - - // /* - // * Should succeed. Return (false, ValidAfter, ValidUntil) // false, MAX, 0 - // * Use a sessionKey that is registered. - // */ - // function testValidateUserOpSessionKey() public { - // // Create an static account wallet and get its address - // address account = staticOpenfortFactory.createAccount(accountAdmin, ""); - - // address sessionKey; - // uint256 sessionKeyPrivKey; - // (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - // vm.prank(accountAdmin); - // UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, MAX_TIME); - - // uint256 validationData = _getValidationData( - // account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - // ); - - // uint256 expectedValidationData = Helpers._packValidationData(false, MAX_TIME, 0); - // assertEq(validationData, expectedValidationData); - // } - - // /* - // * Use a sessionKey that is NOT registered. - // * Should return 1; wrong! - // */ - // function testWrongValidateUserOpSessionKey() public { - // // Create an static account wallet and get its address - // address account = staticOpenfortFactory.createAccount(accountAdmin, ""); - - // address sessionKey; - // uint256 sessionKeyPrivKey; - // (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); - - // address sessionKey2; - // uint256 sessionKeyPrivKey2; - // (sessionKey2, sessionKeyPrivKey2) = makeAddrAndKey("sessionKey2"); - - // vm.prank(accountAdmin); - // UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, MAX_TIME); - - // uint256 validationData = _getValidationData( - // account, sessionKeyPrivKey2, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - // ); - // assertEq(validationData, 1); - // } -} diff --git a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol index 63ab424..d4db1f8 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol @@ -1,114 +1,22 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Test, console} from "lib/forge-std/src/Test.sol"; +import {console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {MockERC721} from "contracts/mock/MockERC721.sol"; import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; -import {SigUtils} from "../../utils/SigUtils.sol"; +import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; -contract EIP6551OpenfortAccountTest is Test { +contract EIP6551OpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; - EntryPoint public entryPoint; ERC6551Registry public erc6551Registry; EIP6551OpenfortAccount public eip6551OpenfortAccount; EIP6551OpenfortAccount public implEIP6551OpenfortAccount; - bytes32 public versionSalt = vm.envBytes32("VERSION_SALT"); - address public account; MockERC721 public mockERC721; - // Testing addresses - address private factoryAdmin; - uint256 private factoryAdminPKey; - - address private accountAdmin; - uint256 private accountAdminPKey; - - address payable private beneficiary = payable(makeAddr("beneficiary")); - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (UserOperation[] memory ops) { - uint256 nonce = entryPoint.getNonce(sender, 0); - - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 500_000, - maxFeePerGas: 0, - maxPriorityFeePerGas: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - - // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - /** * @notice Initialize the StaticOpenfortAccount testing contract. * Scenario: @@ -118,6 +26,7 @@ contract EIP6551OpenfortAccountTest is Test { * - testCounter is the counter used to test userOps */ function setUp() public { + versionSalt = bytes32(0x0); // Setup and fund signers (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); vm.deal(factoryAdmin, 100 ether); @@ -158,9 +67,9 @@ contract EIP6551OpenfortAccountTest is Test { } // deploy a new MockERC721 collection - mockERC721 = new MockERC721(); + mockERC721 = new MockERC721{salt: versionSalt}(); - implEIP6551OpenfortAccount = new EIP6551OpenfortAccount(); + implEIP6551OpenfortAccount = new EIP6551OpenfortAccount{salt: versionSalt}(); address eip6551OpenfortAccountAddress = erc6551Registry.createAccount( address(implEIP6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 @@ -225,9 +134,9 @@ contract EIP6551OpenfortAccountTest is Test { ); EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); - eip6551OpenfortAccount2.initialize(address(entryPoint)); IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); assertEq(address(e), address(entryPoint)); + assertNotEq(address(e), eip6551OpenfortAccountAddress2); } /* diff --git a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol index 0429bde..3251e4c 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol +++ b/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol @@ -2,7 +2,6 @@ pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; -import {SigUtils} from "../../utils/SigUtils.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {MockERC721} from "contracts/mock/MockERC721.sol"; diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index 4814af7..5e30c03 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -1,1694 +1,1694 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Test, console} from "lib/forge-std/src/Test.sol"; +import {console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint, UserOperation, IEntryPoint} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {TestToken} from "account-abstraction/test/TestToken.sol"; import {IPaymaster} from "account-abstraction/interfaces/IPaymaster.sol"; - +import {MockERC20} from "contracts/mock/MockERC20.sol"; import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {OpenfortPaymasterV2} from "contracts/paymaster/OpenfortPaymasterV2.sol"; - -// contract OpenfortPaymasterV2Test is Test { -// using ECDSA for bytes32; - -// EntryPoint public entryPoint; -// UpgradeableOpenfortAccount public staticOpenfortAccount; -// UpgradeableOpenfortFactory public staticOpenfortFactory; -// OpenfortPaymasterV2 public openfortPaymaster; -// address public account; -// TestCounter public testCounter; -// TestToken public testToken; - -// // Testing addresses -// address private factoryAdmin; -// uint256 private factoryAdminPKey; - -// address private paymasterAdmin; -// uint256 private paymasterAdminPKey; - -// address private accountAdmin; -// uint256 private accountAdminPKey; - -// address payable private beneficiary = payable(makeAddr("beneficiary")); - -// uint48 internal constant VALIDUNTIL = 2 ** 48 - 1; -// uint48 internal constant VALIDAFTER = 0; -// uint256 internal constant EXCHANGERATE = 10 ** 3; -// uint256 internal constant MOCKSIG = 2 ** 256 - 1; -// uint256 internal TESTTOKEN_ACCOUNT_PREFUND = 100 * 10 ** 18; - -// error InvalidTokenRecipient(); - -// event PostOpGasUpdated(uint256 oldPostOpGas, uint256 _newPostOpGas); - -// /* -// * Auxiliary function to generate a userOP -// */ -// function _setupUserOp( -// address sender, -// uint256 _signerPKey, -// bytes memory _initCode, -// bytes memory _callDataForEntrypoint, -// bytes memory paymasterAndData -// ) internal returns (UserOperation[] memory ops) { -// // Get user op fields -// UserOperation memory op = UserOperation({ -// sender: sender, -// nonce: entryPoint.getNonce(sender, 0), -// initCode: _initCode, -// callData: _callDataForEntrypoint, -// callGasLimit: 500_000, -// verificationGasLimit: 500_000, -// preVerificationGas: 500_000, -// maxFeePerGas: 1500000000, -// maxPriorityFeePerGas: 1500000000, -// paymasterAndData: paymasterAndData, -// signature: bytes("") -// }); - -// // Sign UserOp -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); -// bytes memory userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// // address recoveredSigner = ECDSA.recover(msgHash, v, r, s); -// // address expectedSigner = vm.addr(_signerPKey); -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(_signerPKey)); - -// op.signature = userOpSignature; - -// // Store UserOp -// ops = new UserOperation[](1); -// ops[0] = op; -// } - -// /* -// * Auxiliary function to generate a userOP using the execute() -// * from the account -// */ -// function _setupUserOpExecute( -// address sender, -// uint256 _signerPKey, -// bytes memory _initCode, -// address _target, -// uint256 _value, -// bytes memory _callData, -// bytes memory paymasterAndData -// ) internal returns (UserOperation[] memory) { -// bytes memory callDataForEntrypoint = -// abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - -// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); -// } - -// /* -// * Auxiliary function to generate a userOP using the executeBatch() -// * from the account -// */ -// function _setupUserOpExecuteBatch( -// address sender, -// uint256 _signerPKey, -// bytes memory _initCode, -// address[] memory _target, -// uint256[] memory _value, -// bytes[] memory _callData, -// bytes memory paymasterAndData -// ) internal returns (UserOperation[] memory) { -// bytes memory callDataForEntrypoint = -// abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - -// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); -// } - -// function mockedPaymasterDataNative(address _depositor) internal pure returns (bytes memory dataEncoded) { -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; -// strategy.depositor = _depositor; -// strategy.erc20Token = address(0); -// strategy.exchangeRate = 0; -// // Looking at the source code, I've found this part was not Packed (filled with 0s) -// dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); -// } - -// function mockedPaymasterDataERC20Dynamic(address _depositor) internal view returns (bytes memory dataEncoded) { -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; -// strategy.depositor = _depositor; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = EXCHANGERATE; -// // Looking at the source code, I've found this part was not Packed (filled with 0s) -// dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); -// } - -// function mockedPaymasterDataERC20Fixed(address _depositor, uint256 _pricePerTransaction) -// internal -// view -// returns (bytes memory dataEncoded) -// { -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; -// strategy.depositor = _depositor; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = _pricePerTransaction; -// // Looking at the source code, I've found this part was not Packed (filled with 0s) -// dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); -// } - -// /** -// * @notice Initialize the UpgradeableOpenfortAccount testing contract. -// * Scenario: -// * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory -// * - paymasterAdmin is the deployer (and owner) of the OpenfortPaymaster -// * - accountAdmin is the account used to deploy new static accounts -// * - entryPoint is the singleton EntryPoint -// * - testCounter is the counter used to test userOps -// */ -// function setUp() public { -// // Setup and fund signers -// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); -// vm.deal(factoryAdmin, 100 ether); -// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); -// vm.deal(accountAdmin, 100 ether); -// (paymasterAdmin, paymasterAdminPKey) = makeAddrAndKey("paymasterAdmin"); -// vm.deal(paymasterAdmin, 100 ether); - -// // If we are in a fork -// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { -// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); -// } -// // If not a fork, deploy entryPoint (at correct address) -// else { -// EntryPoint entryPoint_aux = new EntryPoint(); -// bytes memory code = address(entryPoint_aux).code; -// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); -// vm.etch(targetAddr, code); -// entryPoint = EntryPoint(payable(targetAddr)); -// } -// vm.prank(paymasterAdmin); -// openfortPaymaster = new OpenfortPaymasterV2(IEntryPoint(payable(address(entryPoint))), paymasterAdmin); -// // Paymaster deposits 50 ETH to EntryPoint -// vm.prank(paymasterAdmin); -// openfortPaymaster.deposit{value: 50 ether}(); -// // Paymaster stakes 25 ETH -// vm.prank(paymasterAdmin); -// openfortPaymaster.addStake{value: 25 ether}(1); - -// // deploy account factory -// vm.prank(factoryAdmin); -// staticOpenfortAccount = new UpgradeableOpenfortAccount(); -// vm.prank(factoryAdmin); -// staticOpenfortFactory = -// new UpgradeableOpenfortFactory((payable(vm.envAddress("ENTRY_POINT_ADDRESS"))), address(staticOpenfortAccount)); -// // deploy a new TestCounter -// testCounter = new TestCounter(); -// // deploy a new TestToken (ERC20) and mint 1000 -// testToken = new TestToken(); -// testToken.mint(address(this), 1_000 * 10 ** 18); - -// // Create an static account wallet and get its address -// vm.prank(factoryAdmin); -// account = staticOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); -// } - -// /* -// * Test initial parameters -// * -// */ -// function testInitialParameters() public { -// assertEq(address(openfortPaymaster.entryPoint()), vm.envAddress("ENTRY_POINT_ADDRESS")); -// assertEq(address(openfortPaymaster.owner()), paymasterAdmin); -// } - -// /** -// * Deposit should fail if not the owner -// */ -// function testFailDeposit() public { -// vm.prank(factoryAdmin); -// openfortPaymaster.deposit{value: 50 ether}(); -// } - -// /* -// * Test parsePaymasterAndData() when using the native token -// * -// */ -// function testParsePaymasterDataNative() public { -// // Encode the paymaster data -// bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); - -// // Get the related paymaster data signature -// bytes32 hash = keccak256(dataEncoded); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); - -// // Create the paymasterAndData info -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) - -// ( -// uint48 returnedValidUntil, -// uint48 returnedValidAfter, -// OpenfortPaymasterV2.PolicyStrategy memory strategy, -// bytes memory returnedSignature -// ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); - -// assertEq(returnedValidUntil, VALIDUNTIL); -// assertEq(returnedValidAfter, VALIDAFTER); -// assertEq(strategy.erc20Token, address(0)); -// assertEq(strategy.exchangeRate, 0); -// assertEq(signature, returnedSignature); -// } - -// /* -// * Test parsePaymasterAndData() with an ERC20 dynamic -// * -// */ -// function testParsePaymasterDataERC20() public { -// // Encode the paymaster data -// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - -// // Get the related paymaster data signature -// bytes32 hash = keccak256(dataEncoded); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); - -// // Create the paymasterAndData info -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// console.logBytes(paymasterAndData); - -// ( -// uint48 returnedValidUntil, -// uint48 returnedValidAfter, -// OpenfortPaymasterV2.PolicyStrategy memory strategy, -// bytes memory returnedSignature -// ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); -// assertEq(returnedValidUntil, VALIDUNTIL); -// assertEq(returnedValidAfter, VALIDAFTER); -// assertEq(strategy.erc20Token, address(testToken)); -// assertEq(strategy.exchangeRate, EXCHANGERATE); -// assertEq(signature, returnedSignature); -// } - -// /* -// * The owner (paymasterAdmin) can add and withdraw stake. -// * Others cannot. -// */ -// function testPaymasterStake() public { -// assertEq(paymasterAdmin.balance, 25 ether); - -// // The owner can add stake -// vm.prank(paymasterAdmin); -// openfortPaymaster.addStake{value: 2 ether}(10); -// assertEq(paymasterAdmin.balance, 23 ether); - -// // Others cannot add stake -// vm.expectRevert("Ownable: caller is not the owner"); -// openfortPaymaster.addStake{value: 2}(10); - -// // The owner trying to withdraw stake fails because it has not unlocked -// // The owner can withdraw stake -// vm.prank(paymasterAdmin); -// vm.expectRevert(); -// openfortPaymaster.withdrawStake(payable(paymasterAdmin)); - -// // The owner unlocks the stake -// vm.prank(paymasterAdmin); -// openfortPaymaster.unlockStake(); - -// // The owner trying to unlock fails because it has not passed enought time -// vm.prank(paymasterAdmin); -// vm.expectRevert(); -// openfortPaymaster.withdrawStake(payable(paymasterAdmin)); - -// // Passes 20 blocks... -// skip(20); - -// // The owner can now withdraw stake (the 2 ethers recently staked + the 25 from the SetUp) -// vm.prank(paymasterAdmin); -// openfortPaymaster.withdrawStake(payable(paymasterAdmin)); -// assertEq(paymasterAdmin.balance, 50 ether); -// } - -// /* -// * Complete deposit walkthrough test -// */ -// function testDepositsToPaymaster() public { -// // Initially, the Paymaster has 50 ether deposited -// assertEq(entryPoint.balanceOf(address(openfortPaymaster)), 50 ether); - -// // Directly deposit 1 ETH to EntryPoint on behalf of the Paymaster -// entryPoint.depositTo{value: 1 ether}(address(openfortPaymaster)); -// assertEq(entryPoint.balanceOf(address(openfortPaymaster)), 51 ether); - -// // Cannot deposit to address 0 -// vm.expectRevert(); -// openfortPaymaster.depositFor{value: 0 ether}(address(0)); - -// // Cannot deposit 0 ether -// vm.expectRevert(); -// openfortPaymaster.depositFor{value: 0 ether}(factoryAdmin); - -// // Cannot depositFor using owner -// vm.prank(paymasterAdmin); -// vm.expectRevert(); -// openfortPaymaster.depositFor{value: 1 ether}(paymasterAdmin); - -// // Paymaster deposits 1 ETH to EntryPoint -// openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); - -// // Get the WHOLE deposited amount so far -// assertEq(openfortPaymaster.getDeposit(), 52 ether); -// // Notice that, even though the deposit was made to the EntryPoint for the openfortPaymaster, the deposit is 0: -// assertEq(openfortPaymaster.getDepositFor(address(openfortPaymaster)), 0 ether); - -// // All deposit not made using "depositFor" goes to owner (paymasterAdmin) -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 51 ether); - -// vm.expectRevert("Not Owner: use depositFor() instead"); -// openfortPaymaster.deposit{value: 1 ether}(); -// // Get the WHOLE deposited amount so far -// assertEq(openfortPaymaster.getDeposit(), 52 ether); - -// vm.prank(paymasterAdmin); -// openfortPaymaster.deposit{value: 1 ether}(); -// // Get the WHOLE deposited amount so far -// assertEq(openfortPaymaster.getDeposit(), 53 ether); - -// vm.expectRevert(); -// openfortPaymaster.withdrawTo(payable(paymasterAdmin), 1 ether); -// assertEq(openfortPaymaster.getDeposit(), 53 ether); - -// vm.prank(paymasterAdmin); -// openfortPaymaster.withdrawTo(payable(paymasterAdmin), 1 ether); -// // Get the WHOLE deposited amount so far -// assertEq(openfortPaymaster.getDeposit(), 52 ether); - -// vm.expectRevert(); -// vm.prank(paymasterAdmin); -// openfortPaymaster.withdrawTo(payable(paymasterAdmin), 10000 ether); -// // Get the WHOLE deposited amount so far -// assertEq(openfortPaymaster.getDeposit(), 52 ether); - -// // Let's now use withdrawDepositorTo -// // Owner cannot call it -// vm.expectRevert(); -// vm.prank(paymasterAdmin); -// openfortPaymaster.withdrawDepositorTo(payable(paymasterAdmin), 1 ether); - -// // factoryAdmin can call it -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); -// assertEq(factoryAdmin.balance, 100 ether); -// // but not too much! -// vm.expectRevert(); -// vm.prank(factoryAdmin); -// openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1000 ether); -// // not using address 0! -// vm.expectRevert(); -// vm.prank(factoryAdmin); -// openfortPaymaster.withdrawDepositorTo(payable(address(0)), 1 ether); -// vm.prank(factoryAdmin); -// openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); -// assertEq(factoryAdmin.balance, 101 ether); -// // Deposit of the owner is still 51 -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 51 ether); - -// // Finally, let's test withdrawFromDepositor -// // deposit again using factoryAdmin -// openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); -// // Only owner cannot call it -// vm.expectRevert(); -// openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); - -// vm.expectRevert(); -// vm.prank(paymasterAdmin); -// openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 100 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); - -// vm.prank(paymasterAdmin); -// openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); -// } - -// /* -// * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig length) -// * Should revert -// */ -// function testPaymasterUserOpWrongSigLength() public { -// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, "0x1234"); // This part was packed (not filled with 0s) - -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testCounter), -// 0, -// abi.encodeWithSignature("count()"), -// paymasterAndData -// ); - -// // "ECDSA: invalid signature length" -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); -// } - -// /* -// * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig) -// * Should revert -// */ -// function testPaymasterUserOpWrongSig() public { -// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); // MOCKSIG, "1", MOCKSIG to make sure we send 65 bytes as sig -// UserOperation[] memory userOp = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testCounter), -// 0, -// abi.encodeWithSignature("count()"), -// paymasterAndData -// ); - -// // "AA33 reverted: ECDSA: invalid signature" -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); - -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); -// } - -// /* -// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) -// * Should work -// */ -// function testPaymasterUserOpNativeValidSig() public { -// bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// UserOperation[] memory userOps = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testCounter), -// 0, -// abi.encodeWithSignature("count()"), -// paymasterAndData -// ); -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(0); -// strategy.exchangeRate = 0; -// bytes32 hash; -// { -// // Simulating that the Paymaster gets the userOp and signs it -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); -// // entryPoint.simulateValidation(userOp); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// //Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Test sending a userOp with signature from a wrong address -// * Should not work -// */ -// function testPaymasterUserOpNativeWrongUserSig() public { -// bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// UserOperation[] memory userOps = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testCounter), -// 0, -// abi.encodeWithSignature("count()"), -// paymasterAndData -// ); -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; -// strategy.erc20Token = address(0); -// strategy.exchangeRate = EXCHANGERATE; -// bytes32 hash; -// { -// // Simulating that the factory admin gets the userOp and tries to sign it -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(factoryAdmin, ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(factoryAdminPKey)); - -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// vm.expectRevert(); -// entryPoint.handleOps(userOps, beneficiary); -// // entryPoint.simulateValidation(userOp); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); -// //Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); -// } - -// /* -// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) -// * Using ERC20. Should work -// */ -// function testPaymasterUserOpERC20ValidSigDiffMaxPriorityFeePerGas() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - -// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testToken), -// 0, -// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), -// paymasterAndData -// ); - -// userOps[0].maxPriorityFeePerGas += 1; -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = EXCHANGERATE; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has decreased -// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); -// } - -// /* -// * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) -// * Using ERC20. Should work -// */ -// function testPaymasterUserOpERC20ValidSig() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - -// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testToken), -// 0, -// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), -// paymasterAndData -// ); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = EXCHANGERATE; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has decreased -// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); -// } - -// /* -// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) -// * Using FIXED ERC20. Should work -// */ -// function testPaymasterUserOpERC20FixedValidSig() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); -// uint256 pricePerTransaction = 10 ** 18; - -// bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testToken), -// 0, -// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), -// paymasterAndData -// ); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = pricePerTransaction; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has decreased -// assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); -// } - -// /* -// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) -// * ExecBatch. Using dynamic ERC20. Should work -// */ -// function testPaymasterUserOpERC20ValidSigExecBatch() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - -// assertEq(testCounter.counters(account), 0); - -// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// uint256 count = 2; -// address[] memory targets = new address[](count); -// uint256[] memory values = new uint256[](count); -// bytes[] memory callData = new bytes[](count); - -// targets[0] = address(testToken); -// values[0] = 0; -// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - -// targets[1] = address(testCounter); -// values[1] = 0; -// callData[1] = abi.encodeWithSignature("count()"); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = -// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = EXCHANGERATE; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has decreased -// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) -// * ExecBatch. Using fixed ERC20. Should work -// */ -// function testPaymasterUserOpERC20FixedValidSigExecBatch() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - -// assertEq(testCounter.counters(account), 0); - -// uint256 pricePerTransaction = 10 ** 18; - -// bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// uint256 count = 2; -// address[] memory targets = new address[](count); -// uint256[] memory values = new uint256[](count); -// bytes[] memory callData = new bytes[](count); - -// targets[0] = address(testToken); -// values[0] = 0; -// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - -// targets[1] = address(testCounter); -// values[1] = 0; -// callData[1] = abi.encodeWithSignature("count()"); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = -// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = pricePerTransaction; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has decreased -// assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) -// * ExecBatch. Using fixed ERC20 expensive. Should work -// */ -// function testPaymasterUserOpERC20FixedExpensiveValidSigExecBatch() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - -// assertEq(testCounter.counters(account), 0); - -// uint256 pricePerTransaction = 10; - -// bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// uint256 count = 2; -// address[] memory targets = new address[](count); -// uint256[] memory values = new uint256[](count); -// bytes[] memory callData = new bytes[](count); - -// targets[0] = address(testToken); -// values[0] = 0; -// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - -// targets[1] = address(testCounter); -// values[1] = 0; -// callData[1] = abi.encodeWithSignature("count()"); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = -// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = pricePerTransaction; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has decreased -// assert(testToken.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) -// * ExecBatch. Should work -// */ -// function testPaymasterUserOpNativeValidSigExecBatch() public { -// bytes memory dataEncoded = mockedPaymasterDataNative(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// uint256 count = 2; -// address[] memory targets = new address[](count); -// uint256[] memory values = new uint256[](count); -// bytes[] memory callData = new bytes[](count); - -// targets[0] = address(testToken); -// values[0] = 0; -// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - -// targets[1] = address(testCounter); -// values[1] = 0; -// callData[1] = abi.encodeWithSignature("count()"); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = -// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(0); -// strategy.exchangeRate = 0; - -// bytes32 hash; -// { -// // Simulating that the Paymaster gets the userOp and signs it -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); -// // entryPoint.simulateValidation(userOp); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// //Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); -// } - -// /* -// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) -// * ExecBatch. Using ERC20. Should work. -// * Test showing that failing to repay in ERC20 still spends some of Paymaster's deposit (DoS) -// */ -// function testFailPaymasterUserOpERC20ValidSigExecBatchInsufficientERC20() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, 100); -// assertEq(testToken.balanceOf(account), 100); - -// assertEq(testCounter.counters(account), 0); - -// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// uint256 count = 2; -// address[] memory targets = new address[](count); -// uint256[] memory values = new uint256[](count); -// bytes[] memory callData = new bytes[](count); - -// targets[0] = address(testToken); -// values[0] = 0; -// callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); - -// targets[1] = address(testCounter); -// values[1] = 0; -// callData[1] = abi.encodeWithSignature("count()"); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = -// _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = EXCHANGERATE; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has the same deposit -// assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has not decreased -// assertEq(testToken.balanceOf(account), 100); -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); - -// // If this fails, it would mean: -// // 1- That the paymaster has spent some of its deposit -// // 2- That the smart account could not perform the desired actions, but still has all testTokens -// // An attacker could DoS the paymaster to drain its deposit -// } - -// /* -// * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) -// * Using ERC20. Should work. -// */ -// function testFailPaymasterUserOpERC20ValidSigSmallApprove() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - -// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(paymasterAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testToken), -// 0, -// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 1), -// paymasterAndData -// ); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; -// strategy.depositor = paymasterAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = EXCHANGERATE; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has the same deposit -// assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has not decreased -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); -// // Verify that the counter has not increased -// assertEq(testCounter.counters(account), 0); - -// // If this fails, it would mean: -// // 1- That the paymaster has spent some of its deposit -// // 2- That the smart account could not perform the desired actions, but still has all testTokens -// // An attacker could DoS the paymaster to drain its deposit -// } - -// /* -// * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) -// * Using ERC20 and a 3rd party depositor. Should work -// */ -// function testPaymasterUserOpERC20ValidSigDepositor() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - -// vm.prank(factoryAdmin); -// openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); -// assertEq(openfortPaymaster.getDeposit(), 100 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - -// bytes memory dataEncoded = mockedPaymasterDataERC20Dynamic(factoryAdmin); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testToken), -// 0, -// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), -// paymasterAndData -// ); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; -// strategy.depositor = factoryAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = EXCHANGERATE; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has decreased -// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - -// assert(openfortPaymaster.getDeposit() < 100 ether); -// assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); -// assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // deposit of the owner should have increased a bit because of the dust -// } - -// /* -// * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) -// * Using ERC20 (fixed rate) and a 3rd party depositor. Should work -// */ -// function testPaymasterUserOpERC20FixedValidSigDepositor() public { -// assertEq(testToken.balanceOf(account), 0); -// testToken.mint(account, TESTTOKEN_ACCOUNT_PREFUND); -// assertEq(testToken.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); - -// vm.prank(factoryAdmin); -// openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); -// assertEq(openfortPaymaster.getDeposit(), 100 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - -// uint256 pricePerTransaction = 10 ** 18; - -// bytes memory dataEncoded = mockedPaymasterDataERC20Fixed(factoryAdmin, pricePerTransaction); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); - -// // Create a userOp to let the paymaster use our testTokens -// UserOperation[] memory userOps = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testToken), -// 0, -// abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), -// paymasterAndData -// ); - -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; -// strategy.depositor = factoryAdmin; -// strategy.erc20Token = address(testToken); -// strategy.exchangeRate = pricePerTransaction; - -// // Simulating that the Paymaster gets the userOp and signs it -// bytes32 hash; -// { -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// // Verify that the balance of the smart account has decreased -// assert(testToken.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - -// assert(openfortPaymaster.getDeposit() < 100 ether); -// assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); -// assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // deposit of the owner should have increased a bit because of the dust -// } - -// /* -// * Test setPostOpGas function -// */ -// function testSetPostOpGas() public { -// vm.expectRevert("Ownable: caller is not the owner"); -// openfortPaymaster.setPostOpGas(15_000); - -// vm.prank(paymasterAdmin); -// vm.expectRevert(); -// openfortPaymaster.setPostOpGas(0); - -// // Expect that we will see a PostOpGasUpdated event -// vm.prank(paymasterAdmin); -// vm.expectEmit(true, true, false, false); -// emit PostOpGasUpdated(40_000, 15_000); -// openfortPaymaster.setPostOpGas(15_000); -// } - -// /* -// * Trigger _requireFromEntryPoint() from BaseOpenfortPaymaster -// */ -// function test_requireFromEntryPoint() public { -// UserOperation[] memory userOpAux = _setupUserOpExecute( -// account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()"), "" -// ); - -// vm.prank(paymasterAdmin); -// vm.expectRevert("Sender not EntryPoint"); -// openfortPaymaster.validatePaymasterUserOp(userOpAux[0], bytes32(""), 0); - -// vm.prank(paymasterAdmin); -// vm.expectRevert("Sender not EntryPoint"); -// openfortPaymaster.postOp(IPaymaster.PostOpMode(0), bytes(""), 0); -// } - -// /* -// * Test basic transfer ownership -// */ -// function testAcceptOwnershipBasic() public { -// assertEq(openfortPaymaster.owner(), paymasterAdmin); - -// vm.expectRevert("Ownable: caller is not the owner"); -// openfortPaymaster.transferOwnership(factoryAdmin); - -// vm.prank(paymasterAdmin); -// openfortPaymaster.transferOwnership(factoryAdmin); - -// vm.expectRevert("Ownable2Step: caller is not the new owner"); -// openfortPaymaster.acceptOwnership(); - -// vm.prank(factoryAdmin); -// openfortPaymaster.acceptOwnership(); -// assertEq(openfortPaymaster.owner(), factoryAdmin); -// } - -// /* -// * ToDo Test complex transfer ownership -// */ -// function testAcceptOwnershipComplex() public { -// assertEq(openfortPaymaster.owner(), paymasterAdmin); - -// // Play around with deposits -// assertEq(openfortPaymaster.getDeposit(), 50 ether); -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); - -// openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); - -// assertEq(openfortPaymaster.getDeposit(), 53 ether); -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); - -// vm.prank(paymasterAdmin); -// openfortPaymaster.transferOwnership(factoryAdmin); - -// assertEq(openfortPaymaster.getDeposit(), 53 ether); -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); -// // Play around with deposits - -// vm.prank(factoryAdmin); -// openfortPaymaster.acceptOwnership(); -// assertEq(openfortPaymaster.owner(), factoryAdmin); - -// // After transferring the ownership, the old owner does not have any deposit -// // and the new one has all deposit from previous owner PLUS its old deposit - -// assertEq(openfortPaymaster.getDeposit(), 53 ether); -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 0 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 53 ether); -// } - -// /* -// * Test using a new depositor (factoryAdmin) -// * Should work -// */ -// function testPaymasterUserOpNativeValidSigDEPOSITOR() public { -// bytes memory dataEncoded = mockedPaymasterDataNative(factoryAdmin); - -// assertEq(openfortPaymaster.getDeposit(), 50 ether); -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); - -// openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); - -// assertEq(openfortPaymaster.getDeposit(), 53 ether); -// assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); -// assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); - -// bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); -// console.log("paymasterAndData"); -// console.log("paymasterAndData"); -// console.logBytes(paymasterAndData); - -// UserOperation[] memory userOps = _setupUserOpExecute( -// account, -// accountAdminPKey, -// bytes(""), -// address(testCounter), -// 0, -// abi.encodeWithSignature("count()"), -// paymasterAndData -// ); -// OpenfortPaymasterV2.PolicyStrategy memory strategy; -// strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; -// strategy.depositor = factoryAdmin; -// strategy.erc20Token = address(0); -// strategy.exchangeRate = 0; -// bytes32 hash; -// { -// // Simulating that the Paymaster gets the userOp and signs it -// hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); -// bytes memory signature = abi.encodePacked(r, s, v); -// bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); // This part was packed (not filled with 0s) -// assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); -// userOps[0].paymasterAndData = paymasterAndDataSigned; -// } - -// console.log("userOps[0].paymasterAndData"); -// console.log("userOps[0].paymasterAndData"); -// console.logBytes(userOps[0].paymasterAndData); - -// // Back to the user. Sign the userOp -// bytes memory userOpSignature; -// bytes32 hash2; -// { -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); -// userOpSignature = abi.encodePacked(r, s, v); - -// // Verifications below commented to avoid "Stack too deep" error -// assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); - -// // Should return account admin -// hash2 = -// ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); -// } - -// // The hash of the userOp should not have changed after the inclusion of the sig -// assertEq(hash, hash2); -// userOps[0].signature = userOpSignature; - -// // Get the paymaster deposit before handling the userOp -// uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); - -// entryPoint.handleOps(userOps, beneficiary); -// // entryPoint.simulateValidation(userOp); - -// // Verify that the paymaster has less deposit now -// assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); -// //Verify that the counter has increased -// assertEq(testCounter.counters(account), 1); - -// assert(openfortPaymaster.getDeposit() < 53 ether); // less than 53 because the total cost have decreased -// assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // more than 50 because the dust has gone to the owner deposit -// assert(openfortPaymaster.getDepositFor(factoryAdmin) < 3 ether); // less than 3 because the gas cost was paid using factoryAdmin's deposit -// } -// } +import {OpenfortBaseTest} from "../core/OpenfortBaseTest.t.sol"; + +contract OpenfortPaymasterV2Test is OpenfortBaseTest { + using ECDSA for bytes32; + + OpenfortPaymasterV2 public openfortPaymaster; + + address private paymasterAdmin; + uint256 private paymasterAdminPKey; + + UpgradeableOpenfortAccount public upgradeableOpenfortAccount; + UpgradeableOpenfortFactory public upgradeableOpenfortFactory; + + uint48 internal constant VALIDUNTIL = 2 ** 48 - 1; + uint48 internal constant VALIDAFTER = 0; + uint256 internal constant EXCHANGERATE = 10 ** 3; + uint256 internal constant MOCKSIG = 2 ** 256 - 1; + uint256 internal TESTTOKEN_ACCOUNT_PREFUND = 100 * 10 ** 18; + + error InvalidTokenRecipient(); + + event PostOpGasUpdated(uint256 oldPostOpGas, uint256 _newPostOpGas); + + /* + * Auxiliary function to generate a userOP + */ + function _setupUserOp( + address sender, + uint256 _signerPKey, + bytes memory _initCode, + bytes memory _callDataForEntrypoint, + bytes memory paymasterAndData + ) internal returns (UserOperation[] memory ops) { + // Get user op fields + UserOperation memory op = UserOperation({ + sender: sender, + nonce: entryPoint.getNonce(sender, 0), + initCode: _initCode, + callData: _callDataForEntrypoint, + callGasLimit: 500_000, + verificationGasLimit: 500_000, + preVerificationGas: 500_000, + maxFeePerGas: 1500000000, + maxPriorityFeePerGas: 1500000000, + paymasterAndData: paymasterAndData, + signature: bytes("") + }); + + // Sign UserOp + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); + bytes memory userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + // address recoveredSigner = ECDSA.recover(msgHash, v, r, s); + // address expectedSigner = vm.addr(_signerPKey); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(_signerPKey)); + + op.signature = userOpSignature; + + // Store UserOp + ops = new UserOperation[](1); + ops[0] = op; + } + + /* + * Auxiliary function to generate a userOP using the execute() + * from the account + */ + function _setupUserOpExecute( + address sender, + uint256 _signerPKey, + bytes memory _initCode, + address _target, + uint256 _value, + bytes memory _callData, + bytes memory paymasterAndData + ) internal returns (UserOperation[] memory) { + bytes memory callDataForEntrypoint = + abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); + + return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); + } + + /* + * Auxiliary function to generate a userOP using the executeBatch() + * from the account + */ + function _setupUserOpExecuteBatch( + address sender, + uint256 _signerPKey, + bytes memory _initCode, + address[] memory _target, + uint256[] memory _value, + bytes[] memory _callData, + bytes memory paymasterAndData + ) internal returns (UserOperation[] memory) { + bytes memory callDataForEntrypoint = + abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); + + return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint, paymasterAndData); + } + + function mockPaymasterDataNative(address _depositor) internal pure returns (bytes memory dataEncoded) { + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; + strategy.depositor = _depositor; + strategy.erc20Token = address(0); + strategy.exchangeRate = 0; + // Looking at the source code, I've found this part was not Packed (filled with 0s) + dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); + } + + function mockPaymasterDataERC20Dynamic(address _depositor) internal view returns (bytes memory dataEncoded) { + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; + strategy.depositor = _depositor; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = EXCHANGERATE; + // Looking at the source code, I've found this part was not Packed (filled with 0s) + dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); + } + + function mockPaymasterDataERC20Fixed(address _depositor, uint256 _pricePerTransaction) + internal + view + returns (bytes memory dataEncoded) + { + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; + strategy.depositor = _depositor; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = _pricePerTransaction; + // Looking at the source code, I've found this part was not Packed (filled with 0s) + dataEncoded = abi.encode(VALIDUNTIL, VALIDAFTER, strategy); + } + + /** + * @notice Initialize the UpgradeableOpenfortAccount testing contract. + * Scenario: + * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory + * - paymasterAdmin is the deployer (and owner) of the OpenfortPaymaster + * - accountAdmin is the account used to deploy new static accounts + * - entryPoint is the singleton EntryPoint + * - testCounter is the counter used to test userOps + */ + function setUp() public { + versionSalt = bytes32(0x0); + // Setup and fund signers + (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); + vm.deal(factoryAdmin, 100 ether); + (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); + vm.deal(accountAdmin, 100 ether); + (paymasterAdmin, paymasterAdminPKey) = makeAddrAndKey("paymasterAdmin"); + vm.deal(paymasterAdmin, 100 ether); + (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); + + // If we are in a fork + if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { + entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); + } + // If not a fork, deploy entryPoint (at correct address) + else { + EntryPoint entryPoint_aux = new EntryPoint(); + bytes memory code = address(entryPoint_aux).code; + address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); + vm.etch(targetAddr, code); + entryPoint = EntryPoint(payable(targetAddr)); + } + vm.prank(paymasterAdmin); + openfortPaymaster = + new OpenfortPaymasterV2{salt: versionSalt}(IEntryPoint(payable(address(entryPoint))), paymasterAdmin); + // Paymaster deposits 50 ETH to EntryPoint + vm.prank(paymasterAdmin); + openfortPaymaster.deposit{value: 50 ether}(); + // Paymaster stakes 25 ETH + vm.prank(paymasterAdmin); + openfortPaymaster.addStake{value: 25 ether}(1); + + // deploy account factory + vm.prank(factoryAdmin); + upgradeableOpenfortAccount = new UpgradeableOpenfortAccount{salt: versionSalt}(); + vm.prank(factoryAdmin); + upgradeableOpenfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( + factoryAdmin, + address(entryPoint), + address(upgradeableOpenfortAccount), + RECOVERY_PERIOD, + SECURITY_PERIOD, + SECURITY_WINDOW, + LOCK_PERIOD, + OPENFORT_GUARDIAN + ); + // deploy a new TestCounter + testCounter = new TestCounter(); + // deploy a new MockERC20 and mint 1000 + mockERC20 = new MockERC20(); + mockERC20.mint(address(this), 1_000 * 10 ** 18); + + // Create an static account wallet and get its address + vm.prank(factoryAdmin); + account = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); + } + + /* + * Test initial parameters + * + */ + function testInitialParameters() public { + assertEq(address(openfortPaymaster.entryPoint()), vm.envAddress("ENTRY_POINT_ADDRESS")); + assertEq(address(openfortPaymaster.owner()), paymasterAdmin); + } + + /** + * Deposit should fail if not the owner + */ + function testFailDeposit() public { + vm.prank(factoryAdmin); + openfortPaymaster.deposit{value: 50 ether}(); + } + + /* + * Test parsePaymasterAndData() when using the native token + * + */ + function testParsePaymasterDataNative() public { + // Encode the paymaster data + bytes memory dataEncoded = mockPaymasterDataNative(paymasterAdmin); + + // Get the related paymaster data signature + bytes32 hash = keccak256(dataEncoded); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + + // Create the paymasterAndData info + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + + ( + uint48 returnedValidUntil, + uint48 returnedValidAfter, + OpenfortPaymasterV2.PolicyStrategy memory strategy, + bytes memory returnedSignature + ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); + + assertEq(returnedValidUntil, VALIDUNTIL); + assertEq(returnedValidAfter, VALIDAFTER); + assertEq(strategy.erc20Token, address(0)); + assertEq(strategy.exchangeRate, 0); + assertEq(signature, returnedSignature); + } + + /* + * Test parsePaymasterAndData() with an ERC20 dynamic + * + */ + function testParsePaymasterDataERC20() public { + // Encode the paymaster data + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); + + // Get the related paymaster data signature + bytes32 hash = keccak256(dataEncoded); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + + // Create the paymasterAndData info + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + console.logBytes(paymasterAndData); + + ( + uint48 returnedValidUntil, + uint48 returnedValidAfter, + OpenfortPaymasterV2.PolicyStrategy memory strategy, + bytes memory returnedSignature + ) = openfortPaymaster.parsePaymasterAndData(paymasterAndData); + assertEq(returnedValidUntil, VALIDUNTIL); + assertEq(returnedValidAfter, VALIDAFTER); + assertEq(strategy.erc20Token, address(mockERC20)); + assertEq(strategy.exchangeRate, EXCHANGERATE); + assertEq(signature, returnedSignature); + } + + /* + * The owner (paymasterAdmin) can add and withdraw stake. + * Others cannot. + */ + function testPaymasterStake() public { + assertEq(paymasterAdmin.balance, 25 ether); + + // The owner can add stake + vm.prank(paymasterAdmin); + openfortPaymaster.addStake{value: 2 ether}(10); + assertEq(paymasterAdmin.balance, 23 ether); + + // Others cannot add stake + vm.expectRevert("Ownable: caller is not the owner"); + openfortPaymaster.addStake{value: 2}(10); + + // The owner trying to withdraw stake fails because it has not unlocked + // The owner can withdraw stake + vm.prank(paymasterAdmin); + vm.expectRevert(); + openfortPaymaster.withdrawStake(payable(paymasterAdmin)); + + // The owner unlocks the stake + vm.prank(paymasterAdmin); + openfortPaymaster.unlockStake(); + + // The owner trying to unlock fails because it has not passed enought time + vm.prank(paymasterAdmin); + vm.expectRevert(); + openfortPaymaster.withdrawStake(payable(paymasterAdmin)); + + // Passes 20 blocks... + skip(20); + + // The owner can now withdraw stake (the 2 ethers recently staked + the 25 from the SetUp) + vm.prank(paymasterAdmin); + openfortPaymaster.withdrawStake(payable(paymasterAdmin)); + assertEq(paymasterAdmin.balance, 50 ether); + } + + /* + * Complete deposit walkthrough test + */ + function testDepositsToPaymaster() public { + // Initially, the Paymaster has 50 ether deposited + assertEq(entryPoint.balanceOf(address(openfortPaymaster)), 50 ether); + + // Directly deposit 1 ETH to EntryPoint on behalf of the Paymaster + entryPoint.depositTo{value: 1 ether}(address(openfortPaymaster)); + assertEq(entryPoint.balanceOf(address(openfortPaymaster)), 51 ether); + + // Cannot deposit to address 0 + vm.expectRevert(); + openfortPaymaster.depositFor{value: 0 ether}(address(0)); + + // Cannot deposit 0 ether + vm.expectRevert(); + openfortPaymaster.depositFor{value: 0 ether}(factoryAdmin); + + // Cannot depositFor using owner + vm.prank(paymasterAdmin); + vm.expectRevert(); + openfortPaymaster.depositFor{value: 1 ether}(paymasterAdmin); + + // Paymaster deposits 1 ETH to EntryPoint + openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + + // Get the WHOLE deposited amount so far + assertEq(openfortPaymaster.getDeposit(), 52 ether); + // Notice that, even though the deposit was made to the EntryPoint for the openfortPaymaster, the deposit is 0: + assertEq(openfortPaymaster.getDepositFor(address(openfortPaymaster)), 0 ether); + + // All deposit not made using "depositFor" goes to owner (paymasterAdmin) + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 51 ether); + + vm.expectRevert("Not Owner: use depositFor() instead"); + openfortPaymaster.deposit{value: 1 ether}(); + // Get the WHOLE deposited amount so far + assertEq(openfortPaymaster.getDeposit(), 52 ether); + + vm.prank(paymasterAdmin); + openfortPaymaster.deposit{value: 1 ether}(); + // Get the WHOLE deposited amount so far + assertEq(openfortPaymaster.getDeposit(), 53 ether); + + vm.expectRevert(); + openfortPaymaster.withdrawTo(payable(paymasterAdmin), 1 ether); + assertEq(openfortPaymaster.getDeposit(), 53 ether); + + vm.prank(paymasterAdmin); + openfortPaymaster.withdrawTo(payable(paymasterAdmin), 1 ether); + // Get the WHOLE deposited amount so far + assertEq(openfortPaymaster.getDeposit(), 52 ether); + + vm.expectRevert(); + vm.prank(paymasterAdmin); + openfortPaymaster.withdrawTo(payable(paymasterAdmin), 10000 ether); + // Get the WHOLE deposited amount so far + assertEq(openfortPaymaster.getDeposit(), 52 ether); + + // Let's now use withdrawDepositorTo + // Owner cannot call it + vm.expectRevert(); + vm.prank(paymasterAdmin); + openfortPaymaster.withdrawDepositorTo(payable(paymasterAdmin), 1 ether); + + // factoryAdmin can call it + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + assertEq(factoryAdmin.balance, 100 ether); + // but not too much! + vm.expectRevert(); + vm.prank(factoryAdmin); + openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1000 ether); + // not using address 0! + vm.expectRevert(); + vm.prank(factoryAdmin); + openfortPaymaster.withdrawDepositorTo(payable(address(0)), 1 ether); + vm.prank(factoryAdmin); + openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); + assertEq(factoryAdmin.balance, 101 ether); + // Deposit of the owner is still 51 + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 51 ether); + + // Finally, let's test withdrawFromDepositor + // deposit again using factoryAdmin + openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + // Only owner cannot call it + vm.expectRevert(); + openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + + vm.expectRevert(); + vm.prank(paymasterAdmin); + openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 100 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + + vm.prank(paymasterAdmin); + openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); + } + + /* + * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig length) + * Should revert + */ + function testPaymasterUserOpWrongSigLength() public { + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, "0x1234"); + + UserOperation[] memory userOp = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(testCounter), + 0, + abi.encodeWithSignature("count()"), + paymasterAndData + ); + + // "ECDSA: invalid signature length" + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + + // Verify that the counter has not increased + assertEq(testCounter.counters(account), 0); + } + + /* + * Test sending a userOp with an invalid paymasterAndData (valid paymaster, but invalid sig) + * Should revert + */ + function testPaymasterUserOpWrongSig() public { + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); // MOCKSIG, "1", MOCKSIG to make sure we send 65 bytes as sig + UserOperation[] memory userOp = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(testCounter), + 0, + abi.encodeWithSignature("count()"), + paymasterAndData + ); + + // "AA33 reverted: ECDSA: invalid signature" + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + + // Verify that the counter has not increased + assertEq(testCounter.counters(account), 0); + } + + /* + * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) + * Should work + */ + function testPaymasterUserOpNativeValidSig() public { + bytes memory dataEncoded = mockPaymasterDataNative(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + UserOperation[] memory userOps = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(testCounter), + 0, + abi.encodeWithSignature("count()"), + paymasterAndData + ); + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(0); + strategy.exchangeRate = 0; + bytes32 hash; + { + // Simulating that the Paymaster gets the userOp and signs it + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + // entryPoint.simulateValidation(userOp); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + //Verify that the counter has increased + assertEq(testCounter.counters(account), 1); + } + + /* + * Test sending a userOp with signature from a wrong address + * Should not work + */ + function testPaymasterUserOpNativeWrongUserSig() public { + bytes memory dataEncoded = mockPaymasterDataNative(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + UserOperation[] memory userOps = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(testCounter), + 0, + abi.encodeWithSignature("count()"), + paymasterAndData + ); + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; + strategy.erc20Token = address(0); + strategy.exchangeRate = EXCHANGERATE; + bytes32 hash; + { + // Simulating that the factory admin gets the userOp and tries to sign it + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(factoryAdmin, ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(factoryAdminPKey)); + + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + vm.expectRevert(); + entryPoint.handleOps(userOps, beneficiary); + // entryPoint.simulateValidation(userOp); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); + //Verify that the counter has not increased + assertEq(testCounter.counters(account), 0); + } + + /* + * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) + * Using ERC20. Should work + */ + function testPaymasterUserOpERC20ValidSigDiffMaxPriorityFeePerGas() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(mockERC20), + 0, + abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), + paymasterAndData + ); + + userOps[0].maxPriorityFeePerGas += 1; + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = EXCHANGERATE; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has decreased + assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + } + + /* + * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) + * Using ERC20. Should work + */ + function testPaymasterUserOpERC20ValidSig() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(mockERC20), + 0, + abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), + paymasterAndData + ); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = EXCHANGERATE; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has decreased + assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + } + + /* + * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) + * Using FIXED ERC20. Should work + */ + function testPaymasterUserOpERC20FixedValidSig() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + uint256 pricePerTransaction = 10 ** 18; + + bytes memory dataEncoded = mockPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(mockERC20), + 0, + abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), + paymasterAndData + ); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = pricePerTransaction; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has decreased + assert(mockERC20.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); + } + + /* + * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) + * ExecBatch. Using dynamic ERC20. Should work + */ + function testPaymasterUserOpERC20ValidSigExecBatch() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + + assertEq(testCounter.counters(account), 0); + + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + uint256 count = 2; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + targets[0] = address(mockERC20); + values[0] = 0; + callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + + targets[1] = address(testCounter); + values[1] = 0; + callData[1] = abi.encodeWithSignature("count()"); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = + _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = EXCHANGERATE; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has decreased + assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + assertEq(testCounter.counters(account), 1); + } + + /* + * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) + * ExecBatch. Using fixed ERC20. Should work + */ + function testPaymasterUserOpERC20FixedValidSigExecBatch() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + + assertEq(testCounter.counters(account), 0); + + uint256 pricePerTransaction = 10 ** 18; + + bytes memory dataEncoded = mockPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + uint256 count = 2; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + targets[0] = address(mockERC20); + values[0] = 0; + callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + + targets[1] = address(testCounter); + values[1] = 0; + callData[1] = abi.encodeWithSignature("count()"); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = + _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = pricePerTransaction; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has decreased + assert(mockERC20.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); + assertEq(testCounter.counters(account), 1); + } + + /* + * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) + * ExecBatch. Using fixed ERC20 expensive. Should work + */ + function testPaymasterUserOpERC20FixedExpensiveValidSigExecBatch() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + + assertEq(testCounter.counters(account), 0); + + uint256 pricePerTransaction = 10; + + bytes memory dataEncoded = mockPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + uint256 count = 2; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + targets[0] = address(mockERC20); + values[0] = 0; + callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + + targets[1] = address(testCounter); + values[1] = 0; + callData[1] = abi.encodeWithSignature("count()"); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = + _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = pricePerTransaction; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has decreased + assert(mockERC20.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); + assertEq(testCounter.counters(account), 1); + } + + /* + * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) + * ExecBatch. Should work + */ + function testPaymasterUserOpNativeValidSigExecBatch() public { + bytes memory dataEncoded = mockPaymasterDataNative(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + uint256 count = 2; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + targets[0] = address(mockERC20); + values[0] = 0; + callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + + targets[1] = address(testCounter); + values[1] = 0; + callData[1] = abi.encodeWithSignature("count()"); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = + _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(0); + strategy.exchangeRate = 0; + + bytes32 hash; + { + // Simulating that the Paymaster gets the userOp and signs it + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + // entryPoint.simulateValidation(userOp); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + //Verify that the counter has increased + assertEq(testCounter.counters(account), 1); + } + + /* + * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) + * ExecBatch. Using ERC20. Should work. + * Test showing that failing to repay in ERC20 still spends some of Paymaster's deposit (DoS) + */ + function testFailPaymasterUserOpERC20ValidSigExecBatchInsufficientERC20() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, 100); + assertEq(mockERC20.balanceOf(account), 100); + + assertEq(testCounter.counters(account), 0); + + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + uint256 count = 2; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + targets[0] = address(mockERC20); + values[0] = 0; + callData[0] = abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1); + + targets[1] = address(testCounter); + values[1] = 0; + callData[1] = abi.encodeWithSignature("count()"); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = + _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = EXCHANGERATE; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has the same deposit + assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has not decreased + assertEq(mockERC20.balanceOf(account), 100); + // Verify that the counter has not increased + assertEq(testCounter.counters(account), 0); + + // If this fails, it would mean: + // 1- That the paymaster has spent some of its deposit + // 2- That the smart account could not perform the desired actions, but still has all mockERC20s + // An attacker could DoS the paymaster to drain its deposit + } + + /* + * Test sending a userOp with an valid paymasterAndData (valid paymaster, valid sig) + * Using ERC20. Should work. + */ + function testFailPaymasterUserOpERC20ValidSigSmallApprove() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(mockERC20), + 0, + abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 1), + paymasterAndData + ); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; + strategy.depositor = paymasterAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = EXCHANGERATE; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has the same deposit + assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has not decreased + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + // Verify that the counter has not increased + assertEq(testCounter.counters(account), 0); + + // If this fails, it would mean: + // 1- That the paymaster has spent some of its deposit + // 2- That the smart account could not perform the desired actions, but still has all mockERC20s + // An attacker could DoS the paymaster to drain its deposit + } + + /* + * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) + * Using ERC20 and a 3rd party depositor. Should work + */ + function testPaymasterUserOpERC20ValidSigDepositor() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + + vm.prank(factoryAdmin); + openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); + assertEq(openfortPaymaster.getDeposit(), 100 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); + + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(factoryAdmin); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(mockERC20), + 0, + abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), + paymasterAndData + ); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; + strategy.depositor = factoryAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = EXCHANGERATE; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has decreased + assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + + assert(openfortPaymaster.getDeposit() < 100 ether); + assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); + // deposit of the owner should have increased a bit because of the dust + assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); + } + + /* + * Test sending a userOp with a valid paymasterAndData (valid paymaster, valid sig) + * Using ERC20 (fixed rate) and a 3rd party depositor. Should work + */ + function testPaymasterUserOpERC20FixedValidSigDepositor() public { + assertEq(mockERC20.balanceOf(account), 0); + mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + + vm.prank(factoryAdmin); + openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); + assertEq(openfortPaymaster.getDeposit(), 100 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); + + uint256 pricePerTransaction = 10 ** 18; + + bytes memory dataEncoded = mockPaymasterDataERC20Fixed(factoryAdmin, pricePerTransaction); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + + // Create a userOp to let the paymaster use our mockERC20s + UserOperation[] memory userOps = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(mockERC20), + 0, + abi.encodeWithSignature("approve(address,uint256)", address(openfortPaymaster), 2 ** 256 - 1), + paymasterAndData + ); + + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; + strategy.depositor = factoryAdmin; + strategy.erc20Token = address(mockERC20); + strategy.exchangeRate = pricePerTransaction; + + // Simulating that the Paymaster gets the userOp and signs it + bytes32 hash; + { + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + // Verify that the balance of the smart account has decreased + assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + + assert(openfortPaymaster.getDeposit() < 100 ether); + assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); + assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // deposit of the owner should have increased a bit because of the dust + } + + /* + * Test setPostOpGas function + */ + function testSetPostOpGas() public { + vm.expectRevert("Ownable: caller is not the owner"); + openfortPaymaster.setPostOpGas(15_000); + + vm.prank(paymasterAdmin); + vm.expectRevert(); + openfortPaymaster.setPostOpGas(0); + + // Expect that we will see a PostOpGasUpdated event + vm.prank(paymasterAdmin); + vm.expectEmit(true, true, false, false); + emit PostOpGasUpdated(40_000, 15_000); + openfortPaymaster.setPostOpGas(15_000); + } + + /* + * Trigger _requireFromEntryPoint() from BaseOpenfortPaymaster + */ + function test_requireFromEntryPoint() public { + UserOperation[] memory userOpAux = _setupUserOpExecute( + account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()"), "" + ); + + vm.prank(paymasterAdmin); + vm.expectRevert("Sender not EntryPoint"); + openfortPaymaster.validatePaymasterUserOp(userOpAux[0], bytes32(""), 0); + + vm.prank(paymasterAdmin); + vm.expectRevert("Sender not EntryPoint"); + openfortPaymaster.postOp(IPaymaster.PostOpMode(0), bytes(""), 0); + } + + /* + * Test basic transfer ownership + */ + function testAcceptOwnershipBasic() public { + assertEq(openfortPaymaster.owner(), paymasterAdmin); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortPaymaster.transferOwnership(factoryAdmin); + + vm.prank(paymasterAdmin); + openfortPaymaster.transferOwnership(factoryAdmin); + + vm.expectRevert("Ownable2Step: caller is not the new owner"); + openfortPaymaster.acceptOwnership(); + + vm.prank(factoryAdmin); + openfortPaymaster.acceptOwnership(); + assertEq(openfortPaymaster.owner(), factoryAdmin); + } + + /* + * ToDo Test complex transfer ownership + */ + function testAcceptOwnershipComplex() public { + assertEq(openfortPaymaster.owner(), paymasterAdmin); + + // Play around with deposits + assertEq(openfortPaymaster.getDeposit(), 50 ether); + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); + + openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); + + assertEq(openfortPaymaster.getDeposit(), 53 ether); + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); + + vm.prank(paymasterAdmin); + openfortPaymaster.transferOwnership(factoryAdmin); + + assertEq(openfortPaymaster.getDeposit(), 53 ether); + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); + // Play around with deposits + + vm.prank(factoryAdmin); + openfortPaymaster.acceptOwnership(); + assertEq(openfortPaymaster.owner(), factoryAdmin); + + // After transferring the ownership, the old owner does not have any deposit + // and the new one has all deposit from previous owner PLUS its old deposit + + assertEq(openfortPaymaster.getDeposit(), 53 ether); + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 0 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 53 ether); + } + + /* + * Test using a new depositor (factoryAdmin) + * Should work + */ + function testPaymasterUserOpNativeValidSigDEPOSITOR() public { + bytes memory dataEncoded = mockPaymasterDataNative(factoryAdmin); + + assertEq(openfortPaymaster.getDeposit(), 50 ether); + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); + + openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); + + assertEq(openfortPaymaster.getDeposit(), 53 ether); + assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); + + bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); + console.log("paymasterAndData"); + console.log("paymasterAndData"); + console.logBytes(paymasterAndData); + + UserOperation[] memory userOps = _setupUserOpExecute( + account, + accountAdminPKey, + bytes(""), + address(testCounter), + 0, + abi.encodeWithSignature("count()"), + paymasterAndData + ); + OpenfortPaymasterV2.PolicyStrategy memory strategy; + strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; + strategy.depositor = factoryAdmin; + strategy.erc20Token = address(0); + strategy.exchangeRate = 0; + bytes32 hash; + { + // Simulating that the Paymaster gets the userOp and signs it + hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(paymasterAdminPKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); + assertEq(openfortPaymaster.owner(), ECDSA.recover(hash, signature)); + userOps[0].paymasterAndData = paymasterAndDataSigned; + } + + console.log("userOps[0].paymasterAndData"); + console.log("userOps[0].paymasterAndData"); + console.logBytes(userOps[0].paymasterAndData); + + // Back to the user. Sign the userOp + bytes memory userOpSignature; + bytes32 hash2; + { + bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + userOpSignature = abi.encodePacked(r, s, v); + + // Verifications below commented to avoid "Stack too deep" error + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + + // Should return account admin + hash2 = + ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); + } + + // The hash of the userOp should not have changed after the inclusion of the sig + assertEq(hash, hash2); + userOps[0].signature = userOpSignature; + + // Get the paymaster deposit before handling the userOp + uint256 paymasterDepositBefore = openfortPaymaster.getDeposit(); + + entryPoint.handleOps(userOps, beneficiary); + // entryPoint.simulateValidation(userOp); + + // Verify that the paymaster has less deposit now + assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); + //Verify that the counter has increased + assertEq(testCounter.counters(account), 1); + + assert(openfortPaymaster.getDeposit() < 53 ether); // less than 53 because the total cost have decreased + assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // more than 50 because the dust has gone to the owner deposit + assert(openfortPaymaster.getDepositFor(factoryAdmin) < 3 ether); // less than 3 because the gas cost was paid using factoryAdmin's deposit + } +} diff --git a/test/foundry/utils/SigUtils.sol b/test/foundry/utils/SigUtils.sol deleted file mode 100644 index 85099a8..0000000 --- a/test/foundry/utils/SigUtils.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.19; - -/* - * Based on Foundry's tutorial: https://book.getfoundry.sh/tutorials/testing-eip712 - * ToDo to clean the main testing contracts - */ -contract SigUtils { - bytes32 private constant _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - - constructor() {} -} From c4700e2e2087340a25c00e824356eedeffd404d5 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 21 Nov 2023 12:30:48 +0100 Subject: [PATCH 28/83] Renaming EIP6551 to ERC6551 --- contracts/core/README.md | 17 +- .../ERC6551OpenfortAccount.sol} | 4 +- script/deployEIP6551.s.sol | 8 +- .../EIP6551OpenfortAccountTest.t.sol | 110 +++++------ .../EIP6551OpenfortBenchmark.t.sol | 180 +++++++++--------- .../core/{eip6551 => erc6551}/README.md | 14 +- 6 files changed, 171 insertions(+), 162 deletions(-) rename contracts/core/{eip6551/EIP6551OpenfortAccount.sol => erc6551/ERC6551OpenfortAccount.sol} (97%) rename test/foundry/core/{eip6551 => erc6551}/EIP6551OpenfortAccountTest.t.sol (65%) rename test/foundry/core/{eip6551 => erc6551}/EIP6551OpenfortBenchmark.t.sol (79%) rename test/foundry/core/{eip6551 => erc6551}/README.md (72%) diff --git a/contracts/core/README.md b/contracts/core/README.md index b9b2f1a..ebcce3d 100644 --- a/contracts/core/README.md +++ b/contracts/core/README.md @@ -1,8 +1,11 @@ -# Recoverable Accounts +# Openfort Accounts + + +## Recoverable Accounts Recoverable accounts are a special type of accounts that support social recovery. They let owners define a set of guardians to help recover the account in case the private key of the owner is compromised (forgotten, disclosed, stolen...). -## Context +### Context As explained by Vitalik in a famous post (https://vitalik.ca/general/2021/01/11/recovery.html), guardians are the next thing after multisig wallets. @@ -14,7 +17,7 @@ Observe the image below from the mentioned blog post to visualize how guardians If enough guardians confirm the recovery of an account, they can help the legitimate owner of the account update the signing key (aka owner). -## How do Recoverable Accounts Work +### How do Recoverable Accounts Work The owner can: - Propose a new guardian. @@ -34,10 +37,16 @@ Anyone can: - When in recovery mode, submit the list of needed signatures (from half of the guardians) to complete the recovery of the account. -## More Information +### More Information - https://vitalik.ca/general/2021/01/11/recovery.html - https://old.reddit.com/r/ethereum/comments/11tijiv/how_i_think_about_choosing_guardians_for_multisig/ - https://medium.com/nightlycrypto/smart-wallets-guardians-756d27a749c7 - https://www.makeuseof.com/what-is-crypto-social-recovery-wallet-how-does-it-work/ - https://support.argent.xyz/hc/en-us/articles/360022631992-About-guardians - https://docs-wallet.loopring.io/security/guardians + +## ERC6551 Accounts + +### Context + +### How do ERC6551 Accounts work diff --git a/contracts/core/eip6551/EIP6551OpenfortAccount.sol b/contracts/core/erc6551/ERC6551OpenfortAccount.sol similarity index 97% rename from contracts/core/eip6551/EIP6551OpenfortAccount.sol rename to contracts/core/erc6551/ERC6551OpenfortAccount.sol index ebe0d07..b8a5915 100644 --- a/contracts/core/eip6551/EIP6551OpenfortAccount.sol +++ b/contracts/core/erc6551/ERC6551OpenfortAccount.sol @@ -11,14 +11,14 @@ import {ERC6551AccountLib} from "erc6551/src/lib/ERC6551AccountLib.sol"; import {BaseOpenfortAccount, IEntryPoint, ECDSAUpgradeable} from "../base/BaseOpenfortAccount.sol"; /** - * @title EIP6551OpenfortAccount (Non-upgradeable) + * @title ERC6551OpenfortAccount (Non-upgradeable) * @notice Smart contract wallet with session keys following the ERC-4337 and EIP-6551 standards. * It inherits from: * - BaseOpenfortAccount to comply with ERC-4337 * - IERC6551Account to have permissions using ERC-721 tokens * - IERC6551Executable */ -contract EIP6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC6551Executable { +contract ERC6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC6551Executable { using ECDSAUpgradeable for bytes32; address internal entrypointContract; diff --git a/script/deployEIP6551.s.sol b/script/deployEIP6551.s.sol index 04024a2..38b1cce 100644 --- a/script/deployEIP6551.s.sol +++ b/script/deployEIP6551.s.sol @@ -4,10 +4,10 @@ pragma solidity =0.8.19; import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; import {MockERC721} from "contracts/mock/MockERC721.sol"; -import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; +import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; import {IERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; -contract EIP6551OpenfortDeploy is Script { +contract ERC6551OpenfortDeploy is Script { uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); address internal deployAddress = vm.addr(deployPrivKey); IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); @@ -19,7 +19,7 @@ contract EIP6551OpenfortDeploy is Script { vm.startBroadcast(deployPrivKey); // Create an acccount to serve as implementation - EIP6551OpenfortAccount eip6551OpenfortAccount = new EIP6551OpenfortAccount{salt: versionSalt}(); + ERC6551OpenfortAccount erc6551OpenfortAccount = new ERC6551OpenfortAccount{salt: versionSalt}(); uint256 chainId; assembly { @@ -31,7 +31,7 @@ contract EIP6551OpenfortDeploy is Script { // The first call should create a new account, while the second will just return the corresponding account address address account2 = - erc6551Registry.createAccount(address(eip6551OpenfortAccount), versionSalt, chainId, address(nft721), 1); + erc6551Registry.createAccount(address(erc6551OpenfortAccount), versionSalt, chainId, address(nft721), 1); console.log("Registry at address %s has created an account at address %s", address(erc6551Registry), account2); vm.stopBroadcast(); diff --git a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol similarity index 65% rename from test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol rename to test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol index d4db1f8..ce54ee6 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol @@ -6,15 +6,15 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {MockERC721} from "contracts/mock/MockERC721.sol"; -import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; +import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; -contract EIP6551OpenfortAccountTest is OpenfortBaseTest { +contract ERC6551OpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; ERC6551Registry public erc6551Registry; - EIP6551OpenfortAccount public eip6551OpenfortAccount; - EIP6551OpenfortAccount public implEIP6551OpenfortAccount; + ERC6551OpenfortAccount public erc6551OpenfortAccount; + ERC6551OpenfortAccount public implERC6551OpenfortAccount; MockERC721 public mockERC721; /** @@ -69,16 +69,16 @@ contract EIP6551OpenfortAccountTest is OpenfortBaseTest { // deploy a new MockERC721 collection mockERC721 = new MockERC721{salt: versionSalt}(); - implEIP6551OpenfortAccount = new EIP6551OpenfortAccount{salt: versionSalt}(); + implERC6551OpenfortAccount = new ERC6551OpenfortAccount{salt: versionSalt}(); - address eip6551OpenfortAccountAddress = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 + address erc6551OpenfortAccountAddress = erc6551Registry.createAccount( + address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 ); - eip6551OpenfortAccount = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress)); - eip6551OpenfortAccount.initialize(address(entryPoint)); + erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress)); + erc6551OpenfortAccount.initialize(address(entryPoint)); - mockERC721.mint(eip6551OpenfortAccountAddress, 1); + mockERC721.mint(erc6551OpenfortAccountAddress, 1); vm.stopPrank(); } @@ -87,7 +87,7 @@ contract EIP6551OpenfortAccountTest is OpenfortBaseTest { * Test reinitialize. It should fail. */ function testFailReinitialize() public { - eip6551OpenfortAccount.initialize(address(entryPoint)); + erc6551OpenfortAccount.initialize(address(entryPoint)); } /* @@ -95,12 +95,12 @@ contract EIP6551OpenfortAccountTest is OpenfortBaseTest { */ function testERC6551Deploy() public { address deployedAccount = - erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), 0, block.chainid, address(0), 0); + erc6551Registry.createAccount(address(implERC6551OpenfortAccount), 0, block.chainid, address(0), 0); assertTrue(deployedAccount != address(0)); address predictedAccount = - erc6551Registry.account(address(implEIP6551OpenfortAccount), 0, block.chainid, address(0), 0); + erc6551Registry.account(address(implERC6551OpenfortAccount), 0, block.chainid, address(0), 0); assertEq(predictedAccount, deployedAccount); } @@ -109,7 +109,7 @@ contract EIP6551OpenfortAccountTest is OpenfortBaseTest { * Test initialize implementation. It should fail. */ function testFailInitializeImplementation() public { - implEIP6551OpenfortAccount.initialize(address(entryPoint)); + implERC6551OpenfortAccount.initialize(address(entryPoint)); } /* @@ -117,7 +117,7 @@ contract EIP6551OpenfortAccountTest is OpenfortBaseTest { * EntryPoint address should be 0. Should pass. */ function testImplementationNoEntryPointAddr() public { - IEntryPoint e = implEIP6551OpenfortAccount.entryPoint(); + IEntryPoint e = implERC6551OpenfortAccount.entryPoint(); assertEq(address(e), address(0)); } @@ -129,14 +129,14 @@ contract EIP6551OpenfortAccountTest is OpenfortBaseTest { assembly { chainId := chainid() } - address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), bytes32(0), chainId, address(mockERC721), 1 + address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( + address(implERC6551OpenfortAccount), bytes32(0), chainId, address(mockERC721), 1 ); - EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); - IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); + ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); + IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); assertEq(address(e), address(entryPoint)); - assertNotEq(address(e), eip6551OpenfortAccountAddress2); + assertNotEq(address(e), erc6551OpenfortAccountAddress2); } /* @@ -147,11 +147,11 @@ contract EIP6551OpenfortAccountTest is OpenfortBaseTest { assembly { chainId := chainid() } - address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 + address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( + address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 ); - EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); - IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); + ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); + IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); assertEq(address(e), address(entryPoint)); } @@ -164,55 +164,55 @@ contract EIP6551OpenfortAccountTest is OpenfortBaseTest { assembly { chainId := chainid() } - address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implEIP6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 + address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( + address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 ); - EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); - eip6551OpenfortAccount2.initialize(address(entryPoint)); + ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); + erc6551OpenfortAccount2.initialize(address(entryPoint)); } /* * Test getDeposit() function. - * First ERC4337 function called by this EIP6551-compatible account. + * First ERC4337 function called by this ERC6551-compatible account. */ function testGetDeposit() public { uint256 deposit; - deposit = eip6551OpenfortAccount.getDeposit(); + deposit = erc6551OpenfortAccount.getDeposit(); assertEq(deposit, 0); // We can add deposit by directly calling the EntryPoint - entryPoint.depositTo{value: 1}(address(eip6551OpenfortAccount)); - deposit = eip6551OpenfortAccount.getDeposit(); + entryPoint.depositTo{value: 1}(address(erc6551OpenfortAccount)); + deposit = erc6551OpenfortAccount.getDeposit(); assertEq(deposit, 1); // We can ALSO add deposit by calling the EntryPoint addDeposit() function of the account - eip6551OpenfortAccount.addDeposit{value: 1}(); - deposit = eip6551OpenfortAccount.getDeposit(); + erc6551OpenfortAccount.addDeposit{value: 1}(); + deposit = erc6551OpenfortAccount.getDeposit(); assertEq(deposit, 2); } /* * Test owner() function. - * Check that the owner of the eip6551 account is the owner of the NFT + * Check that the owner of the erc6551 account is the owner of the NFT */ function testOwner() public { - assertEq(eip6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); - assertEq(eip6551OpenfortAccount.owner(), address(eip6551OpenfortAccount)); + assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); + assertEq(erc6551OpenfortAccount.owner(), address(erc6551OpenfortAccount)); } /* * Test owner() function. - * Check that the owner of the eip6551 account is the owner of the NFT + * Check that the owner of the erc6551 account is the owner of the NFT */ function testNotOwner() public { // Burning the NFT - vm.prank(address(eip6551OpenfortAccount)); - mockERC721.transferFrom(address(eip6551OpenfortAccount), address(1), 1); + vm.prank(address(erc6551OpenfortAccount)); + mockERC721.transferFrom(address(erc6551OpenfortAccount), address(1), 1); - assertEq(eip6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); - assertNotEq(eip6551OpenfortAccount.owner(), address(eip6551OpenfortAccount)); - assertEq(eip6551OpenfortAccount.owner(), address(1)); + assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); + assertNotEq(erc6551OpenfortAccount.owner(), address(erc6551OpenfortAccount)); + assertEq(erc6551OpenfortAccount.owner(), address(1)); } /* @@ -226,36 +226,36 @@ contract EIP6551OpenfortAccountTest is OpenfortBaseTest { // Get the counterfactual address vm.prank(factoryAdmin); - address eip6551OpenfortAccountAddress2 = - erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); + address erc6551OpenfortAccountAddress2 = + erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); // Expect that we will see an event containing the account and admin // vm.expectEmit(true, true, false, true); // emit IERC6551Registry.ERC6551AccountCreated( - // eip6551OpenfortAccountAddress2, address(eip6551OpenfortAccount), chainId, address(mockERC721), 1, 2 + // erc6551OpenfortAccountAddress2, address(erc6551OpenfortAccount), chainId, address(mockERC721), 1, 2 // ); // Deploy a static account to the counterfactual address vm.prank(factoryAdmin); - erc6551Registry.createAccount(address(eip6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); + erc6551Registry.createAccount(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); assertEq( - eip6551OpenfortAccountAddress2, - erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) + erc6551OpenfortAccountAddress2, + erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) ); // assertNotEq( - // eip6551OpenfortAccountAddress2, - // erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) + // erc6551OpenfortAccountAddress2, + // erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) // ); assertNotEq( - eip6551OpenfortAccountAddress2, - erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) + erc6551OpenfortAccountAddress2, + erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) ); assertNotEq( - eip6551OpenfortAccountAddress2, - erc6551Registry.account(address(eip6551OpenfortAccount), versionSalt, chainId, address(0), 1) + erc6551OpenfortAccountAddress2, + erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(0), 1) ); } } diff --git a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol b/test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol similarity index 79% rename from test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol rename to test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol index 3251e4c..371c226 100644 --- a/test/foundry/core/eip6551/EIP6551OpenfortBenchmark.t.sol +++ b/test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol @@ -11,9 +11,9 @@ import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/Upgradeable import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {ERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; -import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAccount.sol"; +import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; -// contract EIP6551OpenfortBenchmark is Test { +// contract ERC6551OpenfortBenchmark is Test { // using ECDSA for bytes32; // EntryPoint public entryPoint; @@ -23,8 +23,8 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // UpgradeableOpenfortAccount public upgradeableOpenfortAccount; // ERC6551Registry public erc6551Registry; -// EIP6551OpenfortAccount public eip6551OpenfortAccount; -// EIP6551OpenfortAccount implEIP6551OpenfortAccount; +// ERC6551OpenfortAccount public erc6551OpenfortAccount; +// ERC6551OpenfortAccount implERC6551OpenfortAccount; // uint256 public chainId; @@ -41,8 +41,8 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // address public upgradeableOpenfortAddressComplex; // UpgradeableOpenfortAccount public upgradeableOpenfortAccountComplex; -// address public eip6551OpenfortAddressComplex; -// EIP6551OpenfortAccount public eip6551OpenfortAccountComplex; +// address public erc6551OpenfortAddressComplex; +// ERC6551OpenfortAccount public erc6551OpenfortAccountComplex; // address payable private beneficiary = payable(makeAddr("beneficiary")); @@ -183,15 +183,15 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // // deploy a new MockERC721 collection // nft721 = new MockERC721(); -// implEIP6551OpenfortAccount = new EIP6551OpenfortAccount(); +// implERC6551OpenfortAccount = new ERC6551OpenfortAccount(); // erc6551Registry = new ERC6551Registry(); -// address eip6551OpenfortAccountAddress = -// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(nft721), 1, 1, ""); +// address erc6551OpenfortAccountAddress = +// erc6551Registry.createAccount(address(implERC6551OpenfortAccount), chainId, address(nft721), 1, 1, ""); -// eip6551OpenfortAccount = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress)); -// eip6551OpenfortAccount.initialize(address(entryPoint)); +// erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress)); +// erc6551OpenfortAccount.initialize(address(entryPoint)); // nft721.mint(accountAdmin, 1); @@ -205,9 +205,9 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // nft721.mint(upgradeableOpenfortAddressComplex, 2); -// eip6551OpenfortAddressComplex = -// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(nft721), 2, 2, ""); -// eip6551OpenfortAccountComplex = EIP6551OpenfortAccount(payable(eip6551OpenfortAddressComplex)); +// erc6551OpenfortAddressComplex = +// erc6551Registry.createAccount(address(implERC6551OpenfortAccount), chainId, address(nft721), 2, 2, ""); +// erc6551OpenfortAccountComplex = ERC6551OpenfortAccount(payable(erc6551OpenfortAddressComplex)); // vm.stopPrank(); // } @@ -225,32 +225,32 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // } // /* -// * Create a 2nd EIP6551 account with nft721 as the owner and initialize later +// * Create a 2nd ERC6551 account with nft721 as the owner and initialize later // */ -// function test1CreateEIP6551AccountInitAfter() public { -// address eip6551OpenfortAccountAddress2 = -// erc6551Registry.createAccount(address(implEIP6551OpenfortAccount), chainId, address(nft721), 1, 2, ""); +// function test1CreateERC6551AccountInitAfter() public { +// address erc6551OpenfortAccountAddress2 = +// erc6551Registry.createAccount(address(implERC6551OpenfortAccount), chainId, address(nft721), 1, 2, ""); -// EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); -// eip6551OpenfortAccount2.initialize(address(entryPoint)); -// IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); +// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); +// erc6551OpenfortAccount2.initialize(address(entryPoint)); +// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); // assertEq(address(e), address(entryPoint)); // } // /* -// * Create a 2nd EIP6551 account with nft721 as the owner and initialize during creation +// * Create a 2nd ERC6551 account with nft721 as the owner and initialize during creation // */ -// function test1CreateEIP6551AccountInitDuringCreation() public { -// address eip6551OpenfortAccountAddress2 = erc6551Registry.createAccount( -// address(implEIP6551OpenfortAccount), +// function test1CreateERC6551AccountInitDuringCreation() public { +// address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( +// address(implERC6551OpenfortAccount), // chainId, // address(nft721), // 3, // 1, // abi.encodeWithSignature("initialize(address)", address(entryPoint)) // ); -// EIP6551OpenfortAccount eip6551OpenfortAccount2 = EIP6551OpenfortAccount(payable(eip6551OpenfortAccountAddress2)); -// IEntryPoint e = eip6551OpenfortAccount2.entryPoint(); +// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); +// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); // assertEq(address(e), address(entryPoint)); // } @@ -264,10 +264,10 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // /* // * Test owner() function. -// * Check that the owner of the eip6551 account is the owner of the NFT +// * Check that the owner of the erc6551 account is the owner of the NFT // */ -// function test2OwnerEIP6551() public { -// assertEq(eip6551OpenfortAccount.owner(), nft721.ownerOf(1)); +// function test2OwnerERC6551() public { +// assertEq(erc6551OpenfortAccount.owner(), nft721.ownerOf(1)); // } // /* @@ -289,31 +289,31 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // } // /* -// * Test transferOwnership() function using EIP6551 accounts. +// * Test transferOwnership() function using ERC6551 accounts. // */ -// function test3TransferOwnerEIP6551() public { -// assertEq(eip6551OpenfortAccount.owner(), accountAdmin); +// function test3TransferOwnerERC6551() public { +// assertEq(erc6551OpenfortAccount.owner(), accountAdmin); // vm.prank(accountAdmin); // nft721.safeTransferFrom(accountAdmin, factoryAdmin, 1); -// assertEq(eip6551OpenfortAccount.owner(), factoryAdmin); +// assertEq(erc6551OpenfortAccount.owner(), factoryAdmin); // } // /* -// * Test transferOwnership() function using EIP6551 account with a userOp +// * Test transferOwnership() function using ERC6551 account with a userOp // * It will fail because the msg.sender doing the transferFrom() is the EntryPoint // * the user should do it with a regular TX or approving the EntryPoint to spend // */ -// function testFailTransferOwnerEIP6551UserOp() public { -// assertEq(eip6551OpenfortAccount.owner(), accountAdmin); +// function testFailTransferOwnerERC6551UserOp() public { +// assertEq(erc6551OpenfortAccount.owner(), accountAdmin); // address _target = address(nft721); // bytes memory _callData = // abi.encodeWithSignature("transferFrom(address,address,uint256)", accountAdmin, factoryAdmin, 1); // UserOperation[] memory userOp = _setupUserOpExecute( -// address(eip6551OpenfortAccount), +// address(erc6551OpenfortAccount), // accountAdminPKey, // bytes(""), // address(nft721), @@ -321,13 +321,13 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // abi.encodeWithSignature("execute(address,uint256,bytes)", _target, 0, _callData) // ); -// entryPoint.depositTo{value: 1000000000000000000}(address(eip6551OpenfortAccount)); +// entryPoint.depositTo{value: 1000000000000000000}(address(erc6551OpenfortAccount)); // vm.expectRevert(); // entryPoint.simulateValidation(userOp[0]); // entryPoint.handleOps(userOp, beneficiary); -// assertEq(eip6551OpenfortAccount.owner(), factoryAdmin); +// assertEq(erc6551OpenfortAccount.owner(), factoryAdmin); // } // /* @@ -352,24 +352,24 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // } // /* -// * Test transfer funds function using EIP6551 accounts. +// * Test transfer funds function using ERC6551 accounts. // */ -// function test4TransferFundsEIP6551() public { -// address eip6551OpenfortAddress = payable(address((eip6551OpenfortAccount))); +// function test4TransferFundsERC6551() public { +// address erc6551OpenfortAddress = payable(address((erc6551OpenfortAccount))); // console.log("Admin balance: ", accountAdmin.balance); -// console.log("EIP6551 Openfort Account balance: ", eip6551OpenfortAddress.balance); +// console.log("ERC6551 Openfort Account balance: ", erc6551OpenfortAddress.balance); // vm.prank(accountAdmin); -// (bool ok,) = eip6551OpenfortAddress.call{value: 50 ether}(""); +// (bool ok,) = erc6551OpenfortAddress.call{value: 50 ether}(""); // assert(ok); // console.log("Admin balance: ", accountAdmin.balance); -// console.log("EIP6551 Openfort Account balance: ", eip6551OpenfortAddress.balance); +// console.log("ERC6551 Openfort Account balance: ", erc6551OpenfortAddress.balance); // vm.prank(accountAdmin); -// eip6551OpenfortAccount.execute(accountAdmin, 40 ether, ""); +// erc6551OpenfortAccount.execute(accountAdmin, 40 ether, ""); // console.log("Admin balance: ", accountAdmin.balance); -// console.log("EIP6551 Openfort Account balance: ", eip6551OpenfortAddress.balance); +// console.log("ERC6551 Openfort Account balance: ", erc6551OpenfortAddress.balance); // } // /* @@ -394,24 +394,24 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // } // /* -// * Test transfer ERC20 function using EIP6551 accounts. +// * Test transfer ERC20 function using ERC6551 accounts. // */ -// function test5TransferERC20EIP6551() public { -// address eip6551OpenfortAddress = payable(address((eip6551OpenfortAccount))); +// function test5TransferERC20ERC6551() public { +// address erc6551OpenfortAddress = payable(address((erc6551OpenfortAccount))); // console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); // vm.prank(accountAdmin); -// testUSDC.transfer(eip6551OpenfortAddress, 50 ether); +// testUSDC.transfer(erc6551OpenfortAddress, 50 ether); // console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); +// console.log("ERC6551 Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); // vm.prank(accountAdmin); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 50 ether) // ); // console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); +// console.log("ERC6551 Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); // } // /* @@ -465,72 +465,72 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // } // /* -// * Test multiple transfers ERC20 function using EIP6551 accounts. +// * Test multiple transfers ERC20 function using ERC6551 accounts. // */ -// function test6TransferMultipleERC20EIP6551() public { -// address eip6551OpenfortAddress = payable(address((eip6551OpenfortAccount))); +// function test6TransferMultipleERC20ERC6551() public { +// address erc6551OpenfortAddress = payable(address((erc6551OpenfortAccount))); // console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); +// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); // vm.prank(accountAdmin); -// testUSDC.transfer(eip6551OpenfortAddress, 50 ether); +// testUSDC.transfer(erc6551OpenfortAddress, 50 ether); // console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); +// console.log("ERC6551 Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); // vm.startPrank(accountAdmin); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); -// eip6551OpenfortAccount.execute( +// erc6551OpenfortAccount.execute( // address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) // ); // vm.stopPrank(); // console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("EIP6551 Openfort Account balance: ", testUSDC.balanceOf(eip6551OpenfortAddress)); +// console.log("ERC6551 Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); // } // /* // * Test owner() function. -// * Check that the owner of the eip6551 account is the owner of the NFT +// * Check that the owner of the erc6551 account is the owner of the NFT // */ // function test7ComplexOwner() public { -// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); // } // /* -// * Test transferOwnership() function using upgradeable account that have EIP6551 accounts. +// * Test transferOwnership() function using upgradeable account that have ERC6551 accounts. // * Scenario: a complex account changes the ownership; all NFTs are manageable by the new owner // */ // function test8TransferOwner4337Complex() public { // // The EOA is the owner of the Upgradeable account // assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); -// // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT -// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// // The upgradeable account is the owner of the ERC6551 accounts because it holds the NFT +// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); // assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); // vm.prank(accountAdmin); @@ -543,19 +543,19 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // upgradeableOpenfortAccountComplex.acceptOwnership(); // assertEq(upgradeableOpenfortAccountComplex.owner(), factoryAdmin); -// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); // assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); // } // /* -// * Test transferOwnership() function using upgradeable account that have EIP6551 accounts. -// * Scenario: a complex account transfer an NFT to send an EIP6551 account to another user +// * Test transferOwnership() function using upgradeable account that have ERC6551 accounts. +// * Scenario: a complex account transfer an NFT to send an ERC6551 account to another user // */ -// function test9TransferOwnerEIP6551Complex() public { +// function test9TransferOwnerERC6551Complex() public { // // The EOA is the owner of the Upgradeable account // assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); -// // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT -// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// // The upgradeable account is the owner of the ERC6551 accounts because it holds the NFT +// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); // assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); // vm.prank(accountAdmin); @@ -567,17 +567,17 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // ) // ); // assertEq(nft721.ownerOf(2), factoryAdmin); -// assertEq(eip6551OpenfortAccountComplex.owner(), factoryAdmin); +// assertEq(erc6551OpenfortAccountComplex.owner(), factoryAdmin); // } // /* -// * Test transferOwnership() function using upgradeable account that have EIP6551 accounts. +// * Test transferOwnership() function using upgradeable account that have ERC6551 accounts. // */ -// function test9TransferOwnerEIP6551ComplexUserOp() public { +// function test9TransferOwnerERC6551ComplexUserOp() public { // // The EOA is the owner of the Upgradeable account // assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); -// // The upgradeable account is the owner of the EIP6551 accounts because it holds the NFT -// assertEq(eip6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); +// // The upgradeable account is the owner of the ERC6551 accounts because it holds the NFT +// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); // assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); // UserOperation[] memory userOp = _setupUserOpExecute( @@ -597,6 +597,6 @@ import {EIP6551OpenfortAccount} from "contracts/core/eip6551/EIP6551OpenfortAcco // entryPoint.handleOps(userOp, beneficiary); // assertEq(nft721.ownerOf(2), factoryAdmin); -// assertEq(eip6551OpenfortAccountComplex.owner(), factoryAdmin); +// assertEq(erc6551OpenfortAccountComplex.owner(), factoryAdmin); // } // } diff --git a/test/foundry/core/eip6551/README.md b/test/foundry/core/erc6551/README.md similarity index 72% rename from test/foundry/core/eip6551/README.md rename to test/foundry/core/erc6551/README.md index 2d16f42..3ce08d5 100644 --- a/test/foundry/core/eip6551/README.md +++ b/test/foundry/core/erc6551/README.md @@ -1,10 +1,10 @@ -# Upgradeable Openfort Accounts vs EIP6551 Openfort Accounts +# Upgradeable Openfort Accounts vs ERC6551 Openfort Accounts ## Table -The table below represents the difference of gas needed to perform a set of actions using Upgradeable Openfort accounts vs EIP6551 Openfort accounts. The numbers were taken using the version 0.4 of the contracts in August 2023. Even though the account creation of EIP6551 is cheaper (26%), using the: +The table below represents the difference of gas needed to perform a set of actions using Upgradeable Openfort accounts vs ERC6551 Openfort accounts. The numbers were taken using the version 0.4 of the contracts in August 2023. Even though the account creation of ERC6551 is cheaper (26%), using the: -| Action | Upgradeable | EIP6551 | Difference | +| Action | Upgradeable | ERC6551 | Difference | | :---------- | :------------------ | :----------------- | :------------------- | | Check ownership | 14,794 | 18,890 | 27.68% increase | | Transfer ownership | 41,224 | 64,059 | 55.4% increase | @@ -12,15 +12,15 @@ The table below represents the difference of gas needed to perform a set of acti | Transfer ERC20s | 54,796 | 57,338 | 4.6% increase | | Transfer 10 ERC20s | 110,193 | 137,043 | 24.3% increase | -Further "complex" tests were perfomed like the `test9TransferOwnerEIP6551Complex`. -This represents a transfer of an EIP6551 account owned by an Openfort Upgradeable account. +Further "complex" tests were perfomed like the `test9TransferOwnerERC6551Complex`. +This represents a transfer of an ERC6551 account owned by an Openfort Upgradeable account. For that, an NFT owned by the EOA (that is also the owner of the Upgradeable account) is transferred to another EOA. -This costs 83,248 gas (compared to 41,224 of the upgradeable and 64,059 gas of the simple EIP6551 account). +This costs 83,248 gas (compared to 41,224 of the upgradeable and 64,059 gas of the simple ERC6551 account). This is an increase of 102% in relation to the upgradeable accont and 29.96%. ## How to reproduce it ``` - forge test --mc EIP6551OpenfortBenchmark + forge test --mc ERC6551OpenfortBenchmark ``` From b5f3c5e3f74aaf99cad4439f06792374ce6a749b Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 21 Nov 2023 17:51:49 +0100 Subject: [PATCH 29/83] Improving testing --- contracts/core/README.md | 4 + contracts/core/base/BaseOpenfortAccount.sol | 31 +- .../core/base/BaseRecoverableAccount.sol | 16 +- contracts/core/base/TokenCallbackHandler.sol | 52 ++ .../core/erc6551/ERC6551OpenfortAccount.sol | 51 +- .../core/erc6551/ERC6551OpenfortProxy.sol | 42 ++ .../core/managed/ManagedOpenfortAccount.sol | 8 - .../core/managed/ManagedOpenfortFactory.sol | 4 - .../UpgradeableOpenfortAccount.sol | 14 +- .../UpgradeableOpenfortFactory.sol | 5 +- .../mock/MockV2ManagedOpenfortAccount.sol | 42 +- .../mock/MockV2UpgradeableOpenfortAccount.sol | 48 +- .../erc6551/EIP6551OpenfortAccountTest.t.sol | 520 +++++++++--------- test/foundry/core/erc6551/README.md | 4 +- .../managed/ManagedOpenfortAccountTest.t.sol | 213 ++++--- .../UpgradeableOpenfortAccountTest.t.sol | 125 +++-- 16 files changed, 645 insertions(+), 534 deletions(-) create mode 100644 contracts/core/base/TokenCallbackHandler.sol create mode 100644 contracts/core/erc6551/ERC6551OpenfortProxy.sol diff --git a/contracts/core/README.md b/contracts/core/README.md index ebcce3d..a2bb7a4 100644 --- a/contracts/core/README.md +++ b/contracts/core/README.md @@ -1,5 +1,9 @@ # Openfort Accounts +There are three types of Openfort accounts: +1. Upgradeable accounts (with recoverable features) +2. Managed accounts (with recoverable features) +3. ERC6551 accounts (experiment) ## Recoverable Accounts Recoverable accounts are a special type of accounts that support social recovery. diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 05b4677..809bd9d 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -7,8 +7,8 @@ import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/crypt import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import {BaseAccount, UserOperation, IEntryPoint, UserOperationLib} from "account-abstraction/core/BaseAccount.sol"; -import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; import {_packValidationData} from "account-abstraction/core/Helpers.sol"; +import {TokenCallbackHandler} from "./TokenCallbackHandler.sol"; import {OpenfortErrorsAndEvents} from "../../interfaces/OpenfortErrorsAndEvents.sol"; /** @@ -46,7 +46,7 @@ abstract contract BaseOpenfortAccount is * @param validUntil this sessionKey is valid only until this timestamp. * @param limit limit of uses remaining * @param masterSessionKey if set to true, the session key does not have any limitation other than the validity time - * @param whitelising if set to true, the session key has to follow whitelisting rules + * @param whitelisting if set to true, the session key has to follow whitelisting rules * @param whitelist - this session key can only interact with the addresses in the whitelist. */ struct SessionKeyStruct { @@ -54,7 +54,7 @@ abstract contract BaseOpenfortAccount is uint48 validUntil; uint48 limit; bool masterSessionKey; - bool whitelising; + bool whitelisting; mapping(address contractAddress => bool allowed) whitelist; address registrarAddress; } @@ -71,7 +71,7 @@ abstract contract BaseOpenfortAccount is /** * Require the function call went through EntryPoint or owner */ - function _requireFromEntryPointOrOwner() internal view { + function _requireFromEntryPointOrOwner() internal view virtual { if (msg.sender != address(entryPoint()) && msg.sender != owner()) { revert NotOwnerOrEntrypoint(); } @@ -91,14 +91,14 @@ abstract contract BaseOpenfortAccount is /** * Check current account deposit in the entryPoint */ - function getDeposit() public view returns (uint256) { + function getDeposit() public view virtual returns (uint256) { return entryPoint().balanceOf(address(this)); } /* * @notice Return whether a sessionKey is valid. */ - function isValidSessionKey(address _sessionKey, bytes calldata _callData) public returns (bool) { + function isValidSessionKey(address _sessionKey, bytes calldata _callData) public virtual returns (bool) { SessionKeyStruct storage sessionKey = sessionKeys[_sessionKey]; // If not owner and the session key is revoked, return false if (sessionKey.validUntil == 0) return false; @@ -132,7 +132,7 @@ abstract contract BaseOpenfortAccount is } // Only masterSessionKey can reenter // If there is no whitelist or there is, but the target is whitelisted, return true - if (!sessionKey.whitelising || sessionKey.whitelist[toContract]) { + if (!sessionKey.whitelisting || sessionKey.whitelist[toContract]) { return true; } @@ -152,7 +152,7 @@ abstract contract BaseOpenfortAccount is if (toContracts[i] == address(this)) { return false; } // Only masterSessionKey can reenter - if (sessionKey.whitelising && !sessionKey.whitelist[toContracts[i]]) { + if (sessionKey.whitelisting && !sessionKey.whitelist[toContracts[i]]) { return false; } // One contract's not in the sessionKey's whitelist (if any) unchecked { @@ -170,7 +170,7 @@ abstract contract BaseOpenfortAccount is * @notice See EIP-1271 * Owner and session keys need to sign using EIP712. */ - function isValidSignature(bytes32 _hash, bytes memory _signature) public view override returns (bytes4) { + function isValidSignature(bytes32 _hash, bytes memory _signature) public view virtual override returns (bytes4) { bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, _hash)); bytes32 digest = _hashTypedDataV4(structHash); address signer = digest.recover(_signature); @@ -222,7 +222,7 @@ abstract contract BaseOpenfortAccount is /** * Deposit funds for this account in the EntryPoint */ - function addDeposit() public payable { + function addDeposit() public payable virtual { entryPoint().depositTo{value: msg.value}(address(this)); } @@ -232,7 +232,7 @@ abstract contract BaseOpenfortAccount is * @param _amount to withdraw * @notice ONLY the owner can call this function (it's not using _requireFromEntryPointOrOwner()) */ - function withdrawDepositTo(address payable _withdrawAddress, uint256 _amount) external { + function withdrawDepositTo(address payable _withdrawAddress, uint256 _amount) external virtual { _requireFromOwner(); entryPoint().withdrawTo(_withdrawAddress, _amount); } @@ -240,7 +240,7 @@ abstract contract BaseOpenfortAccount is /** * @dev Call a target contract and reverts if it fails. */ - function _call(address _target, uint256 _value, bytes calldata _calldata) internal { + function _call(address _target, uint256 _value, bytes calldata _calldata) internal virtual { (bool success, bytes memory result) = _target.call{value: _value}(_calldata); if (!success) { assembly { @@ -254,6 +254,7 @@ abstract contract BaseOpenfortAccount is */ function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash) internal + virtual override returns (uint256 validationData) { @@ -286,7 +287,7 @@ abstract contract BaseOpenfortAccount is uint48 _validUntil, uint48 _limit, address[] calldata _whitelist - ) public { + ) public virtual { _requireFromEntryPointOrOwner(); require(_whitelist.length < 11, "Whitelist too big"); @@ -299,7 +300,7 @@ abstract contract BaseOpenfortAccount is } if (i != 0) { // If there was some whitelisting, it is not a masterSessionKey - sessionKeys[_key].whitelising = true; + sessionKeys[_key].whitelisting = true; sessionKeys[_key].masterSessionKey = false; } else { if (_limit == ((2 ** 48) - 1)) sessionKeys[_key].masterSessionKey = true; @@ -318,7 +319,7 @@ abstract contract BaseOpenfortAccount is * Revoke a session key from the account * @param _key session key to revoke */ - function revokeSessionKey(address _key) external { + function revokeSessionKey(address _key) external virtual { _requireFromEntryPointOrOwner(); if (sessionKeys[_key].validUntil != 0) { sessionKeys[_key].validUntil = 0; diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 2e72a03..2ddd8b3 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -130,7 +130,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg /** * @notice Helper method to check if a wallet is locked. */ - function isLocked() public view returns (bool) { + function isLocked() public view virtual returns (bool) { return guardiansConfig.lock > block.timestamp; } @@ -138,14 +138,14 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg * @notice Returns the release time of a wallet lock or 0 if the wallet is unlocked. * @return _releaseAfter The epoch time at which the lock will release (in seconds). */ - function getLock() external view returns (uint256 _releaseAfter) { + function getLock() external view virtual returns (uint256 _releaseAfter) { _releaseAfter = isLocked() ? guardiansConfig.lock : 0; } /** * @notice Lets a guardian lock a wallet. */ - function lock() external { + function lock() external virtual { if (!isGuardian(msg.sender)) revert MustBeGuardian(); if (isLocked()) revert AccountLocked(); _setLock(block.timestamp + lockPeriod); @@ -154,7 +154,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg /** * @notice Lets a guardian unlock a locked wallet. */ - function unlock() external { + function unlock() external virtual { if (!isGuardian(msg.sender)) revert MustBeGuardian(); if (!isLocked()) revert AccountNotLocked(); _setLock(0); @@ -176,7 +176,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg * @notice Returns the number of guardians for the Openfort account. * @return the number of guardians. */ - function guardianCount() public view returns (uint256) { + function guardianCount() public view virtual returns (uint256) { return guardiansConfig.guardians.length; } @@ -184,7 +184,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg * @notice Gets the list of guardians for the Openfort account. * @return the list of guardians. */ - function getGuardians() external view returns (address[] memory) { + function getGuardians() external view virtual returns (address[] memory) { address[] memory guardians = new address[](guardiansConfig.guardians.length); uint256 i; for (i; i < guardiansConfig.guardians.length;) { @@ -340,7 +340,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg * Must be confirmed by N guardians, where N = ceil(Nb Guardians / 2). * @param _recoveryAddress The address to which ownership should be transferred. */ - function startRecovery(address _recoveryAddress) external { + function startRecovery(address _recoveryAddress) external virtual { if (!isGuardian(msg.sender)) revert MustBeGuardian(); _requireRecovery(false); if (isGuardian(_recoveryAddress)) revert GuardianCannotBeOwner(); @@ -356,7 +356,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg * @param _signatures Array of guardian signatures concatenated. * @notice The arguments should be ordered by the address of the guardian signing the message */ - function completeRecovery(bytes[] calldata _signatures) external { + function completeRecovery(bytes[] calldata _signatures) external virtual { _requireRecovery(true); if (recoveryDetails.executeAfter > uint64(block.timestamp)) revert OngoingRecovery(); diff --git a/contracts/core/base/TokenCallbackHandler.sol b/contracts/core/base/TokenCallbackHandler.sol new file mode 100644 index 0000000..34a27b0 --- /dev/null +++ b/contracts/core/base/TokenCallbackHandler.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; + +/** + * Token callback handler. + * Handles supported tokens' callbacks, allowing account receiving these tokens. + */ +contract TokenCallbackHandler is IERC777Recipient, IERC721Receiver, IERC1155Receiver { + function tokensReceived(address, address, address, uint256, bytes calldata, bytes calldata) + external + pure + override + {} + + function onERC721Received(address, address, uint256, bytes calldata) + external + view + virtual + override + returns (bytes4) + { + return IERC721Receiver.onERC721Received.selector; + } + + function onERC1155Received(address, address, uint256, uint256, bytes calldata) + external + pure + override + returns (bytes4) + { + return IERC1155Receiver.onERC1155Received.selector; + } + + function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) + external + pure + override + returns (bytes4) + { + return IERC1155Receiver.onERC1155BatchReceived.selector; + } + + function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) { + return interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IERC1155Receiver).interfaceId + || interfaceId == type(IERC165).interfaceId; + } +} diff --git a/contracts/core/erc6551/ERC6551OpenfortAccount.sol b/contracts/core/erc6551/ERC6551OpenfortAccount.sol index b8a5915..76d98eb 100644 --- a/contracts/core/erc6551/ERC6551OpenfortAccount.sol +++ b/contracts/core/erc6551/ERC6551OpenfortAccount.sol @@ -36,9 +36,8 @@ contract ERC6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 * @notice Initialize the smart contract wallet. */ function initialize(address _entrypoint) public initializer { - if (_entrypoint == address(0)) { - revert ZeroAddressNotAllowed(); - } + _requireFromOwner(); + if (_entrypoint == address(0)) revert ZeroAddressNotAllowed(); emit EntryPointUpdated(entrypointContract, _entrypoint); entrypointContract = _entrypoint; __EIP712_init("Openfort", "0.5"); @@ -136,4 +135,50 @@ contract ERC6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 || _interfaceId == type(IERC165).interfaceId ); } + + function onERC721Received(address, address, uint256 receivedTokenId, bytes memory) + external + view + override + returns (bytes4) + { + _revertIfOwnershipCycle(msg.sender, receivedTokenId); + return IERC721Receiver.onERC721Received.selector; + } + + /** + * @dev Helper method to check if a received token is in the ownership chain of the wallet. + * @param receivedTokenAddress The address of the token being received. + * @param receivedTokenId The ID of the token being received. + */ + function _revertIfOwnershipCycle(address receivedTokenAddress, uint256 receivedTokenId) internal view virtual { + (uint256 _chainId, address _contractAddress, uint256 _tokenId) = token(); + require( + _chainId != block.chainid || receivedTokenAddress != _contractAddress || receivedTokenId != _tokenId, + "Cannot own yourself" + ); + + address currentOwner = owner(); + require(currentOwner != address(this), "Token in ownership chain"); + uint256 depth = 0; + while (currentOwner.code.length > 0) { + try IERC6551Account(payable(currentOwner)).token() returns ( + uint256 chainId, address contractAddress, uint256 tokenId + ) { + require( + chainId != block.chainid || contractAddress != receivedTokenAddress || tokenId != receivedTokenId, + "Token in ownership chain" + ); + // Advance up the ownership chain + currentOwner = IERC721(contractAddress).ownerOf(tokenId); + require(currentOwner != address(this), "Token in ownership chain"); + } catch { + break; + } + unchecked { + ++depth; + } + if (depth == 5) revert("Ownership chain too deep"); + } + } } diff --git a/contracts/core/erc6551/ERC6551OpenfortProxy.sol b/contracts/core/erc6551/ERC6551OpenfortProxy.sol new file mode 100644 index 0000000..446c15c --- /dev/null +++ b/contracts/core/erc6551/ERC6551OpenfortProxy.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +error InvalidImplementation(); + +/** + * @title ERC6551OpenfortProxy (Non-upgradeable) + * @notice Contract to create the ERC6551 proxies + * It inherits from: + * - ERC1967Proxy + */ +contract ERC6551OpenfortProxy is ERC1967Proxy { + address internal immutable defaultImplementation; + + constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) { + if (_logic == address(0)) revert InvalidImplementation(); + defaultImplementation = _logic; + } + + // constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) {} + + function implementation() external view returns (address) { + return _implementation(); + } + + function _implementation() internal view virtual override returns (address implementationAddress) { + implementationAddress = _getImplementation(); + if (implementationAddress == address(0)) return defaultImplementation; + } + + function _beforeFallback() internal virtual override { + super._beforeFallback(); + if (msg.data.length == 0) { + if (_getImplementation() == address(0)) { + _upgradeTo(defaultImplementation); + _delegate(defaultImplementation); + } + } + } +} diff --git a/contracts/core/managed/ManagedOpenfortAccount.sol b/contracts/core/managed/ManagedOpenfortAccount.sol index 06e7338..7d5d798 100644 --- a/contracts/core/managed/ManagedOpenfortAccount.sol +++ b/contracts/core/managed/ManagedOpenfortAccount.sol @@ -2,10 +2,6 @@ pragma solidity =0.8.19; import {BaseRecoverableAccount, IEntryPoint} from "../base/BaseRecoverableAccount.sol"; -import { - Ownable2StepUpgradeable, - OwnableUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; /** * @title ManagedOpenfortAccount (Upgradeable via Beacon) @@ -16,10 +12,6 @@ import { contract ManagedOpenfortAccount is BaseRecoverableAccount { address private constant ENTRYPOINTCONTRACT = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; - function owner() public view virtual override returns (address) { - return OwnableUpgradeable.owner(); - } - /** * Return the current EntryPoint */ diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index 4fddbeb..98709b2 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -3,12 +3,8 @@ pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; - -// Smart wallet implementation to use import {ManagedOpenfortAccount} from "./ManagedOpenfortAccount.sol"; import {OpenfortManagedProxy} from "./OpenfortManagedProxy.sol"; - -// Interfaces import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; diff --git a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol index b9d07b8..40cd6c4 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol @@ -3,26 +3,18 @@ pragma solidity =0.8.19; // Base account contract to inherit from and EntryPoint interface import {BaseRecoverableAccount, IEntryPoint} from "../base/BaseRecoverableAccount.sol"; -import { - Ownable2StepUpgradeable, - OwnableUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; /** * @title UpgradeableOpenfortAccount * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. * It inherits from: - * - BaseOpenfortAccount + * - BaseRecoverableAccount * - UUPSUpgradeable */ contract UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { function _authorizeUpgrade(address) internal override onlyOwner {} - function owner() public view virtual override returns (address) { - return super.owner(); - } - /** * Return the current EntryPoint */ @@ -34,9 +26,7 @@ contract UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { * Update the EntryPoint address */ function updateEntryPoint(address _newEntrypoint) external onlyOwner { - if (_newEntrypoint == address(0)) { - revert ZeroAddressNotAllowed(); - } + if (_newEntrypoint == address(0)) revert ZeroAddressNotAllowed(); emit EntryPointUpdated(entrypointContract, _newEntrypoint); entrypointContract = _newEntrypoint; } diff --git a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol index dd1f893..f8f4dee 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol @@ -2,10 +2,7 @@ pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; -import { - Ownable2StepUpgradeable, - OwnableUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {UpgradeableOpenfortAccount, IEntryPoint} from "./UpgradeableOpenfortAccount.sol"; import {OpenfortUpgradeableProxy} from "./OpenfortUpgradeableProxy.sol"; import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; diff --git a/contracts/mock/MockV2ManagedOpenfortAccount.sol b/contracts/mock/MockV2ManagedOpenfortAccount.sol index e606c1f..1abf364 100644 --- a/contracts/mock/MockV2ManagedOpenfortAccount.sol +++ b/contracts/mock/MockV2ManagedOpenfortAccount.sol @@ -1,41 +1,37 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import { - Ownable2StepUpgradeable, - OwnableUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; - -// Base account contract to inherit from -import {BaseOpenfortAccount, IEntryPoint} from "../core/base/BaseOpenfortAccount.sol"; +import {BaseRecoverableAccount, IEntryPoint} from "../core/base/BaseRecoverableAccount.sol"; /** - * @title ManagedOpenfortAccount (Upgradeable via Beacon) + * @title MockV2ManagedOpenfortAccount (Upgradeable via Beacon) * @notice Smart contract wallet managed via Beacon with session keys following the ERC-4337 standard. * It inherits from: - * - BaseOpenfortAccount + * - BaseRecoverableAccount */ -contract MockV2ManagedOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable { - address private constant ENTRYPOINTCONTRACT = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; +contract MockV2ManagedOpenfortAccount is BaseRecoverableAccount { + address private constant ENTRYPOINTCONTRACT = 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF; - /* - * @notice Initialize the smart contract wallet. + /** + * Return the current EntryPoint */ - function initialize(address _defaultAdmin) public initializer { - if (_defaultAdmin == address(0)) { - revert ZeroAddressNotAllowed(); - } - _transferOwnership(_defaultAdmin); + function entryPoint() public pure override returns (IEntryPoint) { + return IEntryPoint(ENTRYPOINTCONTRACT); } - function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { - return OwnableUpgradeable.owner(); + /** + * Disabled method to avoid recoverability + */ + function getLock() external pure override returns (uint256 _releaseAfter) { + (_releaseAfter); + revert("disabled!"); } /** - * Return the current EntryPoint + * Disabled method to avoid recoverability */ - function entryPoint() public pure override returns (IEntryPoint) { - return IEntryPoint(address(0)); + function startRecovery(address _recoveryAddress) external pure override { + (_recoveryAddress); + revert("disabled!"); } } diff --git a/contracts/mock/MockV2UpgradeableOpenfortAccount.sol b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol index 81588ab..3dfb55b 100644 --- a/contracts/mock/MockV2UpgradeableOpenfortAccount.sol +++ b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol @@ -1,46 +1,40 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import { - Ownable2StepUpgradeable, - OwnableUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +// Base account contract to inherit from and EntryPoint interface +import {BaseRecoverableAccount, IEntryPoint} from "../core/base/BaseRecoverableAccount.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -// Base account contract to inherit from -import {BaseOpenfortAccount, IEntryPoint} from "../core/base/BaseOpenfortAccount.sol"; - /** * @title MockV2UpgradeableOpenfortAccount - * @notice Minimal smart contract wallet with session keys following the ERC-4337 standard. + * @notice Mock contract to test upgradeability * It inherits from: - * - BaseOpenfortAccount + * - BaseRecoverableAccount * - UUPSUpgradeable */ -contract MockV2UpgradeableOpenfortAccount is BaseOpenfortAccount, Ownable2StepUpgradeable, UUPSUpgradeable { - address internal entrypointContract; - /* - * @notice Initialize the smart contract wallet. - */ +contract MockV2UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { + function _authorizeUpgrade(address) internal override onlyOwner {} - function initialize(address _defaultAdmin, address _entrypoint) public initializer { - if (_defaultAdmin == address(0) || _entrypoint == address(0)) { - revert ZeroAddressNotAllowed(); - } - _transferOwnership(_defaultAdmin); - entrypointContract = _entrypoint; + /** + * Return the modified EntryPoint + */ + function entryPoint() public pure override returns (IEntryPoint) { + return IEntryPoint(0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF); } - function _authorizeUpgrade(address) internal override onlyOwner {} - - function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { - return OwnableUpgradeable.owner(); + /** + * Update the EntryPoint address + */ + function updateEntryPoint(address _newEntrypoint) external onlyOwner { + if (_newEntrypoint == address(0)) revert ZeroAddressNotAllowed(); + emit EntryPointUpdated(entrypointContract, _newEntrypoint); + entrypointContract = _newEntrypoint; } /** - * Return the current EntryPoint + * Return 42 to demonstrate that the logic has been updated */ - function entryPoint() public pure override returns (IEntryPoint) { - return IEntryPoint(address(0)); + function easterEgg() external pure returns (uint256) { + return 42; } } diff --git a/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol index ce54ee6..e936c1e 100644 --- a/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol @@ -1,261 +1,265 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {console} from "lib/forge-std/src/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; -import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {MockERC721} from "contracts/mock/MockERC721.sol"; -import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; -import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; - -contract ERC6551OpenfortAccountTest is OpenfortBaseTest { - using ECDSA for bytes32; - - ERC6551Registry public erc6551Registry; - ERC6551OpenfortAccount public erc6551OpenfortAccount; - ERC6551OpenfortAccount public implERC6551OpenfortAccount; - MockERC721 public mockERC721; - - /** - * @notice Initialize the StaticOpenfortAccount testing contract. - * Scenario: - * - factoryAdmin is the deployer (and owner) of the mockNFT - * - accountAdmin is the account used to deploy new static accounts - * - entryPoint is the singleton EntryPoint - * - testCounter is the counter used to test userOps - */ - function setUp() public { - versionSalt = bytes32(0x0); - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - - uint256 chainId; - assembly { - chainId := chainid() - } - - vm.startPrank(factoryAdmin); - - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at the correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } - - // If we are in a fork - if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { - erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); - } - // If not a fork, deploy ERC6551 registry (at the correct address) - else { - ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); - bytes memory code = address(ERC6551Registry_aux).code; - address targetAddr = address(vm.envAddress("ERC6551_REGISTRY_ADDRESS")); - vm.etch(targetAddr, code); - erc6551Registry = ERC6551Registry(payable(targetAddr)); - } - - // deploy a new MockERC721 collection - mockERC721 = new MockERC721{salt: versionSalt}(); - - implERC6551OpenfortAccount = new ERC6551OpenfortAccount{salt: versionSalt}(); - - address erc6551OpenfortAccountAddress = erc6551Registry.createAccount( - address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 - ); - - erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress)); - erc6551OpenfortAccount.initialize(address(entryPoint)); - - mockERC721.mint(erc6551OpenfortAccountAddress, 1); - - vm.stopPrank(); - } - - /* - * Test reinitialize. It should fail. - */ - function testFailReinitialize() public { - erc6551OpenfortAccount.initialize(address(entryPoint)); - } - - /* - * Test deploy. Regular, no userOps. - */ - function testERC6551Deploy() public { - address deployedAccount = - erc6551Registry.createAccount(address(implERC6551OpenfortAccount), 0, block.chainid, address(0), 0); - - assertTrue(deployedAccount != address(0)); - - address predictedAccount = - erc6551Registry.account(address(implERC6551OpenfortAccount), 0, block.chainid, address(0), 0); - - assertEq(predictedAccount, deployedAccount); - } - - /* - * Test initialize implementation. It should fail. - */ - function testFailInitializeImplementation() public { - implERC6551OpenfortAccount.initialize(address(entryPoint)); - } - - /* - * Check implementation has not been initialized. - * EntryPoint address should be 0. Should pass. - */ - function testImplementationNoEntryPointAddr() public { - IEntryPoint e = implERC6551OpenfortAccount.entryPoint(); - assertEq(address(e), address(0)); - } - - /* - * Create a 2nd account using the same technique than in setup with a new salt (2). - */ - function testCreate2ndAcc() public { - uint256 chainId; - assembly { - chainId := chainid() - } - address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implERC6551OpenfortAccount), bytes32(0), chainId, address(mockERC721), 1 - ); - - ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); - IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); - assertEq(address(e), address(entryPoint)); - assertNotEq(address(e), erc6551OpenfortAccountAddress2); - } - - /* - * Create a new account using createAccount() and the initializer. - */ - function testCreateAccInitializer() public { - uint256 chainId; - assembly { - chainId := chainid() - } - address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 - ); - ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); - IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); - assertEq(address(e), address(entryPoint)); - } - - /* - * Create a new account using createAccount() and the initializer. - * Test initialize again should fail. - */ - function testFailCreateAccInitializerNoReinit() public { - uint256 chainId; - assembly { - chainId := chainid() - } - address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( - address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 - ); - - ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); - erc6551OpenfortAccount2.initialize(address(entryPoint)); - } - - /* - * Test getDeposit() function. - * First ERC4337 function called by this ERC6551-compatible account. - */ - function testGetDeposit() public { - uint256 deposit; - deposit = erc6551OpenfortAccount.getDeposit(); - assertEq(deposit, 0); - - // We can add deposit by directly calling the EntryPoint - entryPoint.depositTo{value: 1}(address(erc6551OpenfortAccount)); - deposit = erc6551OpenfortAccount.getDeposit(); - assertEq(deposit, 1); - - // We can ALSO add deposit by calling the EntryPoint addDeposit() function of the account - erc6551OpenfortAccount.addDeposit{value: 1}(); - deposit = erc6551OpenfortAccount.getDeposit(); - assertEq(deposit, 2); - } - - /* - * Test owner() function. - * Check that the owner of the erc6551 account is the owner of the NFT - */ - function testOwner() public { - assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); - assertEq(erc6551OpenfortAccount.owner(), address(erc6551OpenfortAccount)); - } - - /* - * Test owner() function. - * Check that the owner of the erc6551 account is the owner of the NFT - */ - function testNotOwner() public { - // Burning the NFT - vm.prank(address(erc6551OpenfortAccount)); - mockERC721.transferFrom(address(erc6551OpenfortAccount), address(1), 1); - - assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); - assertNotEq(erc6551OpenfortAccount.owner(), address(erc6551OpenfortAccount)); - assertEq(erc6551OpenfortAccount.owner(), address(1)); - } - - /* - * Create an account by directly calling the registry. - */ - function testCreateAccountWithNonceViaRegistry() public { - uint256 chainId; - assembly { - chainId := chainid() - } - - // Get the counterfactual address - vm.prank(factoryAdmin); - address erc6551OpenfortAccountAddress2 = - erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); - - // Expect that we will see an event containing the account and admin - // vm.expectEmit(true, true, false, true); - // emit IERC6551Registry.ERC6551AccountCreated( - // erc6551OpenfortAccountAddress2, address(erc6551OpenfortAccount), chainId, address(mockERC721), 1, 2 - // ); - - // Deploy a static account to the counterfactual address - vm.prank(factoryAdmin); - erc6551Registry.createAccount(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); - - // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); - assertEq( - erc6551OpenfortAccountAddress2, - erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) - ); - // assertNotEq( - // erc6551OpenfortAccountAddress2, - // erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) - // ); - assertNotEq( - erc6551OpenfortAccountAddress2, - erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) - ); - assertNotEq( - erc6551OpenfortAccountAddress2, - erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(0), 1) - ); - } -} +// import {console} from "lib/forge-std/src/Test.sol"; +// import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +// import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; +// import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +// import {MockERC721} from "contracts/mock/MockERC721.sol"; +// import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; +// import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; + +// contract ERC6551OpenfortAccountTest is OpenfortBaseTest { +// using ECDSA for bytes32; + +// ERC6551Registry public erc6551Registry; +// ERC6551OpenfortAccount public erc6551OpenfortAccount; +// ERC6551OpenfortAccount public implERC6551OpenfortAccount; +// MockERC721 public mockERC721; + +// /** +// * @notice Initialize the StaticOpenfortAccount testing contract. +// * Scenario: +// * - factoryAdmin is the deployer (and owner) of the mockNFT +// * - accountAdmin is the account used to deploy new static accounts +// * - entryPoint is the singleton EntryPoint +// * - testCounter is the counter used to test userOps +// */ +// function setUp() public { +// versionSalt = bytes32(0x0); +// // Setup and fund signers +// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); +// vm.deal(factoryAdmin, 100 ether); +// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); +// vm.deal(accountAdmin, 100 ether); + +// uint256 chainId; +// assembly { +// chainId := chainid() +// } +// console.log("ChainId:", chainId); + +// vm.startPrank(factoryAdmin); + +// // If we are in a fork +// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { +// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); +// } +// // If not a fork, deploy entryPoint (at the correct address) +// else { +// EntryPoint entryPoint_aux = new EntryPoint(); +// bytes memory code = address(entryPoint_aux).code; +// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); +// vm.etch(targetAddr, code); +// entryPoint = EntryPoint(payable(targetAddr)); +// } + +// // If we are in a fork +// if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { +// erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); +// console.log("Using ERC6551 registry from a fork"); +// } +// // If not a fork, deploy ERC6551 registry (at the correct address) +// else { +// ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); +// bytes memory code = address(ERC6551Registry_aux).code; +// address targetAddr = address(vm.envAddress("ERC6551_REGISTRY_ADDRESS")); +// vm.etch(targetAddr, code); +// erc6551Registry = ERC6551Registry(payable(targetAddr)); +// } + +// // deploy a new MockERC721 collection +// mockERC721 = new MockERC721{salt: versionSalt}(); + +// implERC6551OpenfortAccount = new ERC6551OpenfortAccount{salt: versionSalt}(); + +// address erc6551OpenfortAccountAddress = erc6551Registry.createAccount( +// address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 +// ); + +// erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress)); +// console.log(erc6551OpenfortAccount.state()); +// erc6551OpenfortAccount.initialize(address(entryPoint)); +// console.log(erc6551OpenfortAccount.state()); + +// mockERC721.mint(erc6551OpenfortAccountAddress, 1); + +// vm.stopPrank(); +// } + +// /* +// * Test reinitialize. It should fail. +// */ +// function testFailReinitialize() public { +// erc6551OpenfortAccount.initialize(address(entryPoint)); +// } + +// /* +// * Test deploy. Regular, no userOps. +// */ +// function testERC6551Deploy() public { +// address deployedAccount = +// erc6551Registry.createAccount(address(implERC6551OpenfortAccount), 0, block.chainid, address(0), 0); + +// assertTrue(deployedAccount != address(0)); + +// address predictedAccount = +// erc6551Registry.account(address(implERC6551OpenfortAccount), 0, block.chainid, address(0), 0); + +// assertEq(predictedAccount, deployedAccount); +// } + +// /* +// * Test initialize implementation. It should fail. +// */ +// function testFailInitializeImplementation() public { +// implERC6551OpenfortAccount.initialize(address(entryPoint)); +// } + +// /* +// * Check implementation has not been initialized. +// * EntryPoint address should be 0. Should pass. +// */ +// function testImplementationNoEntryPointAddr() public { +// IEntryPoint e = implERC6551OpenfortAccount.entryPoint(); +// assertEq(address(e), address(0)); +// } + +// /* +// * Create a 2nd account using the same technique than in setup with a new salt (2). +// */ +// function testCreate2ndAcc() public { +// uint256 chainId; +// assembly { +// chainId := chainid() +// } +// address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( +// address(implERC6551OpenfortAccount), bytes32(0), chainId, address(mockERC721), 1 +// ); + +// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); +// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); +// assertEq(address(e), address(entryPoint)); +// assertNotEq(address(e), erc6551OpenfortAccountAddress2); +// } + +// /* +// * Create a new account using createAccount() and the initializer. +// */ +// function testCreateAccInitializer() public { +// uint256 chainId; +// assembly { +// chainId := chainid() +// } +// address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( +// address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 +// ); +// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); +// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); +// assertEq(address(e), address(entryPoint)); +// } + +// /* +// * Create a new account using createAccount() and the initializer. +// * Test initialize again should fail. +// */ +// function testFailCreateAccInitializerNoReinit() public { +// uint256 chainId; +// assembly { +// chainId := chainid() +// } +// address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( +// address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 +// ); + +// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); +// erc6551OpenfortAccount2.initialize(address(entryPoint)); +// } + +// /* +// * Test getDeposit() function. +// * First ERC4337 function called by this ERC6551-compatible account. +// */ +// function testGetDeposit() public { +// uint256 deposit; +// deposit = erc6551OpenfortAccount.getDeposit(); +// assertEq(deposit, 0); + +// // We can add deposit by directly calling the EntryPoint +// entryPoint.depositTo{value: 1}(address(erc6551OpenfortAccount)); +// deposit = erc6551OpenfortAccount.getDeposit(); +// assertEq(deposit, 1); + +// // We can ALSO add deposit by calling the EntryPoint addDeposit() function of the account +// erc6551OpenfortAccount.addDeposit{value: 1}(); +// deposit = erc6551OpenfortAccount.getDeposit(); +// assertEq(deposit, 2); +// } + +// /* +// * Test owner() function. +// * Check that the owner of the erc6551 account is the owner of the NFT +// */ +// function testOwner() public { +// assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); +// assertEq(erc6551OpenfortAccount.owner(), address(erc6551OpenfortAccount)); +// } + +// /* +// * Test owner() function. +// * Check that the owner of the erc6551 account is the owner of the NFT +// */ +// function testNotOwner() public { +// // Burning the NFT +// vm.prank(address(erc6551OpenfortAccount)); +// mockERC721.transferFrom(address(erc6551OpenfortAccount), address(1), 1); + +// assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); +// assertNotEq(erc6551OpenfortAccount.owner(), address(erc6551OpenfortAccount)); +// assertEq(erc6551OpenfortAccount.owner(), address(1)); +// } + +// /* +// * Create an account by directly calling the registry. +// */ +// function testCreateAccountWithNonceViaRegistry() public { +// uint256 chainId; +// assembly { +// chainId := chainid() +// } + +// // Get the counterfactual address +// vm.prank(factoryAdmin); +// address erc6551OpenfortAccountAddress2 = +// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); + +// // Expect that we will see an event containing the account and admin +// // vm.expectEmit(true, true, false, true); +// // emit IERC6551Registry.ERC6551AccountCreated( +// // erc6551OpenfortAccountAddress2, address(erc6551OpenfortAccount), chainId, address(mockERC721), 1, 2 +// // ); + +// // Deploy a static account to the counterfactual address +// vm.prank(factoryAdmin); +// erc6551Registry.createAccount(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); + +// // Make sure the counterfactual address has not been altered +// vm.prank(factoryAdmin); +// assertEq( +// erc6551OpenfortAccountAddress2, +// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) +// ); +// // assertNotEq( +// // erc6551OpenfortAccountAddress2, +// // erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) +// // ); +// assertNotEq( +// erc6551OpenfortAccountAddress2, +// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) +// ); +// assertNotEq( +// erc6551OpenfortAccountAddress2, +// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(0), 1) +// ); +// } +// } diff --git a/test/foundry/core/erc6551/README.md b/test/foundry/core/erc6551/README.md index 3ce08d5..296e16c 100644 --- a/test/foundry/core/erc6551/README.md +++ b/test/foundry/core/erc6551/README.md @@ -12,11 +12,11 @@ The table below represents the difference of gas needed to perform a set of acti | Transfer ERC20s | 54,796 | 57,338 | 4.6% increase | | Transfer 10 ERC20s | 110,193 | 137,043 | 24.3% increase | -Further "complex" tests were perfomed like the `test9TransferOwnerERC6551Complex`. +Further "complex" tests were performed like the `test9TransferOwnerERC6551Complex`. This represents a transfer of an ERC6551 account owned by an Openfort Upgradeable account. For that, an NFT owned by the EOA (that is also the owner of the Upgradeable account) is transferred to another EOA. This costs 83,248 gas (compared to 41,224 of the upgradeable and 64,059 gas of the simple ERC6551 account). -This is an increase of 102% in relation to the upgradeable accont and 29.96%. +This is an increase of 102% in relation to the upgradeable account and 29.96%. ## How to reproduce it diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index ba11a69..bef9807 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -61,12 +61,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { LOCK_PERIOD, OPENFORT_GUARDIAN ); - // Create an static account wallet and get its address + // Create an managed account wallet and get its address account = managedOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); // deploy a new TestCounter - testCounter = new TestCounter(); + testCounter = new TestCounter{salt: versionSalt}(); // deploy a new MockERC20 (ERC20) - mockERC20 = new MockERC20(); + mockERC20 = new MockERC20{salt: versionSalt}(); vm.stopPrank(); } @@ -82,7 +82,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectEmit(true, true, false, true); emit AccountCreated(account2, accountAdmin); - // Deploy a static account to the counterfactual address + // Deploy a managed account to the counterfactual address vm.prank(factoryAdmin); managedOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); @@ -134,10 +134,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Create an account using the factory and make it call count() directly. */ function testIncrementCounterDirect() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); - // Make the admin of the static account wallet (deployer) call "count" + // Make the admin of the managed account wallet (deployer) call "count" vm.prank(accountAdmin); ManagedOpenfortAccount(payable(account)).execute(address(testCounter), 0, abi.encodeWithSignature("count()")); @@ -147,10 +147,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { /* * Create an account by directly calling the factory and make it call count() - * using the execute() function using the EntryPoint (userOp). Leaveraging ERC-4337. + * using the execute() function using the EntryPoint (userOp). Leveraging ERC-4337. */ function testIncrementCounterViaEntrypoint() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -168,10 +168,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { /* * Create an account by directly calling the factory and make it call count() - * using the executeBatching() function using the EntryPoint (userOp). Leaveraging ERC-4337. + * using the executeBatching() function using the EntryPoint (userOp). Leveraging ERC-4337. */ function testIncrementCounterViaEntrypointBatching() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); uint256 count = 3; @@ -201,7 +201,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that is not registered. */ function testFailIncrementCounterViaSessionKeyNotregistered() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -225,7 +225,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Use a sessionKey that is registered. */ function testIncrementCounterViaSessionKey() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -254,7 +254,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * using the EntryPoint (userOp). Then use the sessionKey to count */ function testRegisterSessionKeyViaEntrypoint() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -303,7 +303,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should not be allowed: session keys cannot register new session keys! */ function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -373,7 +373,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that is expired. */ function testIncrementCounterViaSessionKeyExpired() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -403,7 +403,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that is revoked. */ function testFailIncrementCounterViaSessionKeyRevoked() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -432,7 +432,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimit() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -471,7 +471,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -511,7 +511,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to revoke a sessionKey using a non-privileged user */ function testFailRevokeSessionKeyInvalidUser() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -541,7 +541,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Use a sessionKey with whitelisting to call Execute(). */ function testIncrementCounterViaSessionKeyWhitelisting() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -570,7 +570,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to register a sessionKey with a large whitelist. */ function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -598,7 +598,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Use a sessionKey with whitelisting to call ExecuteBatch(). */ function testIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -644,7 +644,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Use a sessionKey with whitelisting to call ExecuteBatch(). */ function testFailIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -690,7 +690,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). */ function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -719,7 +719,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey with invalid whitelisting to call ExecuteBatch(). */ function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -785,10 +785,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin2); ManagedOpenfortAccount(payable(account)).acceptOwnership(); - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); - // Make the admin of the static account wallet (deployer) call "count" + // Make the admin of the managed account wallet (deployer) call "count" vm.prank(accountAdmin2); ManagedOpenfortAccount(payable(account)).execute(address(testCounter), 0, abi.encodeWithSignature("count()")); @@ -809,7 +809,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin2); ManagedOpenfortAccount(payable(account)).acceptOwnership(); - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -829,7 +829,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Test an account with mockERC20 instead of TestCount. */ function testMintTokenAccount() public { - // Verify that the totalSupply is stil 0 + // Verify that the totalSupply is still 0 assertEq(mockERC20.totalSupply(), 0); // Mint 1 to beneficiary @@ -890,7 +890,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Basic test of simulateValidation() to check that it always reverts. */ // function testSimulateValidation() public { - // // Verify that the counter is stil set to 0 + // // Verify that the counter is still set to 0 // assertEq(testCounter.counters(account), 0); // UserOperation[] memory userOp = _setupUserOpExecute( @@ -924,77 +924,55 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // assertEq(testCounter.counters(account), 0); // } - // /* - // * 1- Deploy a factory using the old EntryPoint to create an account. - // * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() - // */ - // function testUpgradeTo() public { - // // Create a managed account wallet using the old implementation and get its address - // vm.prank(factoryAdmin); - // address payable accountOld = payable(managedOpenfortFactory.createAccountWithNonce(accountAdmin, "2")); - // ManagedOpenfortAccount managedAccount = ManagedOpenfortAccount(accountOld); - // assertEq(managedAccount.owner(), accountAdmin); - // assertEq(address(managedAccount.entryPoint()), address(entryPoint)); - - // OpenfortManagedProxy p = OpenfortManagedProxy(payable(account)); - // // Printing account address and the implementation address - // console.log(account); - // console.log(p.implementation()); - - // // Deploy the new implementation - // MockV2ManagedOpenfortAccount newImplementation = new MockV2ManagedOpenfortAccount(); - // address newImplementationAddress = address(newImplementation); - - // vm.expectRevert("Ownable: caller is not the owner"); - // managedOpenfortFactory.upgradeTo(newImplementationAddress); - - // vm.prank(factoryAdmin); - // managedOpenfortFactory.upgradeTo(newImplementationAddress); - - // assertEq(managedOpenfortFactory.accountImplementation(), newImplementationAddress); - // //redundant view call for now (due to factory being the Beacon now) - // assertEq(managedOpenfortFactory.implementation(), newImplementationAddress); + /* + * 1- Deploy a new account implementation with the new EntryPoint address and disabled + * 2- Upgrade the implementation address + */ + function testUpgradeEntryPoint() public { + address newEntryPoint = 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF; - // // Notice that, even though we bind the address to the old implementation, entryPoint() is now 0 - // assertEq(address(managedAccount.entryPoint()), address(0)); + // Check addressess + assertEq(address(managedOpenfortAccount.entryPoint()), address(entryPoint)); - // // Same for new accounts. From now on, they have the new owner(). - // vm.prank(factoryAdmin); - // address payable account3 = payable(managedOpenfortFactory.createAccountWithNonce(accountAdmin, "3")); - // ManagedOpenfortAccount managedAccount3 = ManagedOpenfortAccount(account3); + // Try to use the old and new implementation before upgrade (should always behave with current values) + assertEq(MockV2ManagedOpenfortAccount(payable(account)).getLock(), 0); + vm.expectRevert(MustBeGuardian.selector); + MockV2ManagedOpenfortAccount(payable(account)).startRecovery(address(0)); - // assertEq(address(managedAccount3.entryPoint()), address(0)); + assertEq(ManagedOpenfortAccount(payable(account)).getDeposit(), 0); + assertEq(MockV2ManagedOpenfortAccount(payable(account)).getDeposit(), 0); - // // Printing account address and the implementation address. Impl address should have changed - // console.log(account); - // console.log(p.implementation()); - // } + // Deploy the new account implementation + MockV2ManagedOpenfortAccount mockV2ManagedOpenfortAccount = + new MockV2ManagedOpenfortAccount{salt: versionSalt}(); - /* - * 1- Deploy a factory using the old EntryPoint to create an account. - * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() - */ - // function testUpgradeEntryPoint() public { - // address oldEntryPoint = address(0x0576a174D229E3cFA37253523E645A78A0C91B57); - // address newEntryPoint = vm.envAddress("ENTRY_POINT_ADDRESS"); - // ManagedOpenfortFactory managedOpenfortFactoryOld = new ManagedOpenfortFactory(payable(oldEntryPoint), address(staticOpenfortAccount)); + // Try to upgrade + vm.expectRevert("Ownable: caller is not the owner"); + managedOpenfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); - // // Create an managed account wallet using the old EntryPoint and get its address - // address payable accountOld = payable(managedOpenfortFactoryOld.createAccount(accountAdmin, "")); - // ManagedOpenfortAccount managedAccount = ManagedOpenfortAccount(accountOld); - // assertEq(address(managedAccount.entryPoint()), oldEntryPoint); + // Finally upgrade + vm.prank(factoryAdmin); + managedOpenfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); - // // Deploy a factory using the new EntryPoint - // ManagedOpenfortFactory managedOpenfortFactoryNew = new ManagedOpenfortFactory(payable(newEntryPoint), address(staticOpenfortAccount)); + // Try to use the old and new implementation before upgrade (should always behave with current values) + vm.expectRevert("disabled!"); + MockV2ManagedOpenfortAccount(payable(account)).getLock(); + vm.expectRevert("disabled!"); + ManagedOpenfortAccount(payable(account)).getLock(); - // vm.expectRevert("Ownable: caller is not the owner"); - // openfortBeacon.upgradeTo(newEntryPoint); + vm.expectRevert("disabled!"); + MockV2ManagedOpenfortAccount(payable(account)).startRecovery(address(0)); + vm.expectRevert("disabled!"); + ManagedOpenfortAccount(payable(account)).startRecovery(address(0)); - // vm.prank(factoryAdmin); - // openfortBeacon.upgradeTo(newEntryPoint); + vm.expectRevert(); + ManagedOpenfortAccount(payable(account)).getDeposit(); + vm.expectRevert(); + MockV2ManagedOpenfortAccount(payable(account)).getDeposit(); - // assertEq(address(managedAccount.entryPoint()), newEntryPoint); - // } + // Check that the EntryPoint is now upgraded too + assertEq(address(MockV2ManagedOpenfortAccount(payable(address(account))).entryPoint()), newEntryPoint); + } function testFailIsValidSignature() public { bytes32 hash = keccak256("Signed by Owner"); @@ -1155,7 +1133,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Create a friend EOA address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner + // Trying to propose a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); @@ -1202,7 +1180,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Create a friend EOA address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner + // Trying to propose a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); @@ -1247,7 +1225,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Create a friend EOA address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner + // Trying to propose a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); @@ -1357,7 +1335,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Create a friend EOA address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner + // Trying to propose a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); @@ -1431,7 +1409,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Verify that the number of guardians is still 1 (default) assertEq(openfortAccount.guardianCount(), 1); - // OPENFORT_GUARDIAN account should stil be a guardian + // OPENFORT_GUARDIAN account should still be a guardian assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); } @@ -1519,7 +1497,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.revokeGuardian(friendAccount); - // Trying to revoke a non-existen guardian (random beneficiary address) + // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); vm.prank(accountAdmin); openfortAccount.revokeGuardian(beneficiary); @@ -1530,12 +1508,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(friendAccount); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(friendAccount); - // Anyone can confirm a revokation after security period + // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); openfortAccount.confirmGuardianRevocation(friendAccount); @@ -1586,12 +1564,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - // Anyone can confirm a revokation after security period + // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); @@ -1634,7 +1612,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.revokeGuardian(friendAccount); - // Trying to revoke a non-existen guardian (random beneficiary address) + // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); vm.prank(accountAdmin); openfortAccount.revokeGuardian(beneficiary); @@ -1645,12 +1623,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(friendAccount); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(friendAccount); - // Anyone can confirm a revokation after security period + // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); openfortAccount.confirmGuardianRevocation(friendAccount); @@ -1665,12 +1643,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - // Anyone can confirm a revokation after security period + // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); @@ -1791,10 +1769,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Verify that the number of guardians is now 0 assertEq(openfortAccount.guardianCount(), 0); - // deault (openfort) account should not be a guardian anymore + // default (openfort) account should not be a guardian anymore assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); - // Expect that we will see an event containing the deault (openfort) account and security period + // Expect that we will see an event containing the default (openfort) account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); vm.prank(accountAdmin); @@ -1805,7 +1783,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Verify that the number of guardians is now 1 again assertEq(openfortAccount.guardianCount(), 1); - // deault (openfort) account should be a guardian again + // default (openfort) account should be a guardian again assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); } @@ -1841,7 +1819,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.revokeGuardian(friendAccount); - // Trying to revoke a non-existen guardian (random beneficiary address) + // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); vm.prank(accountAdmin); openfortAccount.revokeGuardian(beneficiary); @@ -1852,7 +1830,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(friendAccount); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(friendAccount); @@ -1900,10 +1878,14 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { skip(SECURITY_PERIOD + 1); openfortAccount.confirmGuardianProposal(friendAccount); + + // Try to confirm a non-existent revocation vm.expectRevert(MustBeGuardian.selector); - openfortAccount.confirmGuardianRevocation(friendAccount2); // Notice this tries to confirm a non-existent revocation! + openfortAccount.confirmGuardianRevocation(friendAccount2); + // Try to confirm a non-existent revocation vm.expectRevert(UnknownRevoke.selector); - openfortAccount.confirmGuardianRevocation(friendAccount); // Notice this tries to confirm a non-existent revocation! + openfortAccount.confirmGuardianRevocation(friendAccount); + vm.prank(accountAdmin); vm.expectRevert(MustBeGuardian.selector); openfortAccount.revokeGuardian(friendAccount2); // Notice this tries to revoke a non-existent guardian! @@ -1913,8 +1895,9 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(friendAccount); // Starting a valid revocation process skip(SECURITY_PERIOD + 1); + // Try to confirm a guardian that is already valid and pending to revoke vm.expectRevert(DuplicatedGuardian.selector); - openfortAccount.confirmGuardianProposal(friendAccount); // Notice this tries to confirm a guardian that is already valid and pending to revoke! + openfortAccount.confirmGuardianProposal(friendAccount); } /** @@ -2113,7 +2096,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { /* * Case: User added 4 guardians and removes the default (Openfort) * One guardian (friend) is used to start a recovery process - * The guardian that initiatied the recovery + another one are used to complete the flow. + * The guardian that initiated the recovery + another one are used to complete the flow. * @notice Remember that signatures need to be ordered by the guardian's address. */ function test4GuardiansNoDefaultCompleteRecovery() public { diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 30a512a..131aa81 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -137,7 +137,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Create an account using the factory and make it call count() directly. */ function testIncrementCounterDirect() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" @@ -152,10 +152,10 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { /* * Create an account by directly calling the factory and make it call count() - * using the execute() function using the EntryPoint (userOp). Leaveraging ERC-4337. + * using the execute() function using the EntryPoint (userOp). Leveraging ERC-4337. */ function testIncrementCounterViaEntrypoint() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -173,10 +173,10 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { /* * Create an account by directly calling the factory and make it call count() - * using the executeBatching() function using the EntryPoint (userOp). Leaveraging ERC-4337. + * using the executeBatching() function using the EntryPoint (userOp). Leveraging ERC-4337. */ function testIncrementCounterViaEntrypointBatching() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); uint256 count = 3; @@ -206,7 +206,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that is not registered. */ function testFailIncrementCounterViaSessionKeyNotregistered() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -230,7 +230,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Use a sessionKey that is registered. */ function testIncrementCounterViaSessionKey() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -259,7 +259,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * using the EntryPoint (userOp). Then use the sessionKey to count */ function testRegisterSessionKeyViaEntrypoint() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -308,7 +308,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should not be allowed: session keys cannot register new session keys! */ function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -379,7 +379,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * using the EntryPoint (userOp). Then use that sessionKey to register a second one */ function testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -445,7 +445,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that is expired. */ function testIncrementCounterViaSessionKeyExpired() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -475,7 +475,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that is revoked. */ function testFailIncrementCounterViaSessionKeyRevoked() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -504,7 +504,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimit() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -543,7 +543,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey that reached its limit. */ function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -583,7 +583,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to revoke a sessionKey using a non-privileged user */ function testFailRevokeSessionKeyInvalidUser() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -613,7 +613,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Use a sessionKey with whitelisting to call Execute(). */ function testIncrementCounterViaSessionKeyWhitelisting() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -642,7 +642,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to register a sessionKey with a large whitelist. */ function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -670,7 +670,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Use a sessionKey with whitelisting to call ExecuteBatch(). */ function testIncrementCounterViaSessionKeyWhitelistingBatch() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -716,7 +716,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). */ function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -745,7 +745,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should fail, try to use a sessionKey with invalid whitelisting to call ExecuteBatch(). */ function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); address sessionKey; @@ -810,7 +810,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin2); UpgradeableOpenfortAccount(payable(account)).acceptOwnership(); - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" @@ -836,7 +836,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin2); UpgradeableOpenfortAccount(payable(account)).acceptOwnership(); - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -856,7 +856,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Test an account with mockERC20 instead of TestCount. */ function testMintTokenAccount() public { - // Verify that the totalSupply is stil 0 + // Verify that the totalSupply is still 0 assertEq(mockERC20.totalSupply(), 0); // Mint 1 to beneficiary @@ -917,7 +917,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Basic test of simulateValidation() to check that it always reverts. */ function testSimulateValidation() public { - // Verify that the counter is stil set to 0 + // Verify that the counter is still set to 0 assertEq(testCounter.counters(account), 0); UserOperation[] memory userOp = _setupUserOpExecute( @@ -942,21 +942,29 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { MockV2UpgradeableOpenfortAccount newAccountImplementation = new MockV2UpgradeableOpenfortAccount(); OpenfortUpgradeableProxy p = OpenfortUpgradeableProxy(payable(account)); // Printing account address and the implementation address - console.log(account); - console.log(p.implementation()); + console.log("Account address (proxy): ", account); + console.log("Implementation address (old): ", p.implementation()); vm.expectRevert("Ownable: caller is not the owner"); UpgradeableOpenfortAccount(payable(account)).upgradeTo(address(newAccountImplementation)); + vm.expectRevert(); + MockV2UpgradeableOpenfortAccount(payable(account)).easterEgg(); + vm.prank(accountAdmin); UpgradeableOpenfortAccount(payable(account)).upgradeTo(address(newAccountImplementation)); - // Notice that, even though we bind the address to the old implementation, entryPoint() is now 0 - assertEq(address(UpgradeableOpenfortAccount(payable(account)).entryPoint()), address(0)); + // Notice that, even though we bind the address to the old implementation, entryPoint() is now changed + assertEq( + address(UpgradeableOpenfortAccount(payable(account)).entryPoint()), + address(0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF) + ); + + assertEq(MockV2UpgradeableOpenfortAccount(payable(account)).easterEgg(), 42); // Printing account address and the implementation address. Impl address should have changed - console.log(account); - console.log(p.implementation()); + console.log("Account address (proxy): ", account); + console.log("Implementation address (new): ", p.implementation()); } /* @@ -1150,7 +1158,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Create a friend EOA address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner + // Trying to propose a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); @@ -1197,7 +1205,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Create a friend EOA address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner + // Trying to propose a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); @@ -1242,7 +1250,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Create a friend EOA address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner + // Trying to propose a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); @@ -1352,7 +1360,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Create a friend EOA address friendAccount = makeAddr("friend"); - // Trying to proposa a guardian not using the owner + // Trying to propose a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); @@ -1426,7 +1434,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Verify that the number of guardians is still 1 (default) assertEq(openfortAccount.guardianCount(), 1); - // OPENFORT_GUARDIAN account should stil be a guardian + // OPENFORT_GUARDIAN account should still be a guardian assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); } @@ -1514,7 +1522,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.revokeGuardian(friendAccount); - // Trying to revoke a non-existen guardian (random beneficiary address) + // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); vm.prank(accountAdmin); openfortAccount.revokeGuardian(beneficiary); @@ -1525,12 +1533,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(friendAccount); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(friendAccount); - // Anyone can confirm a revokation after security period + // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); openfortAccount.confirmGuardianRevocation(friendAccount); @@ -1581,12 +1589,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - // Anyone can confirm a revokation after security period + // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); @@ -1629,7 +1637,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.revokeGuardian(friendAccount); - // Trying to revoke a non-existen guardian (random beneficiary address) + // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); vm.prank(accountAdmin); openfortAccount.revokeGuardian(beneficiary); @@ -1640,12 +1648,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(friendAccount); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(friendAccount); - // Anyone can confirm a revokation after security period + // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); openfortAccount.confirmGuardianRevocation(friendAccount); @@ -1660,12 +1668,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); - // Anyone can confirm a revokation after security period + // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); @@ -1786,10 +1794,10 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Verify that the number of guardians is now 0 assertEq(openfortAccount.guardianCount(), 0); - // deault (openfort) account should not be a guardian anymore + // default (openfort) account should not be a guardian anymore assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); - // Expect that we will see an event containing the deault (openfort) account and security period + // Expect that we will see an event containing the default (openfort) account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); vm.prank(accountAdmin); @@ -1800,7 +1808,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Verify that the number of guardians is now 1 again assertEq(openfortAccount.guardianCount(), 1); - // deault (openfort) account should be a guardian again + // default (openfort) account should be a guardian again assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); } @@ -1836,7 +1844,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.revokeGuardian(friendAccount); - // Trying to revoke a non-existen guardian (random beneficiary address) + // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); vm.prank(accountAdmin); openfortAccount.revokeGuardian(beneficiary); @@ -1847,7 +1855,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(friendAccount); - // Anyone can confirm a revokation. However, the security period has not passed yet + // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); openfortAccount.confirmGuardianRevocation(friendAccount); @@ -1895,10 +1903,14 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { skip(SECURITY_PERIOD + 1); openfortAccount.confirmGuardianProposal(friendAccount); + + // Try to confirm a non-existent revocation vm.expectRevert(MustBeGuardian.selector); - openfortAccount.confirmGuardianRevocation(friendAccount2); // Notice this tries to confirm a non-existent revocation! + openfortAccount.confirmGuardianRevocation(friendAccount2); + // Try to confirm a non-existent revocation vm.expectRevert(UnknownRevoke.selector); - openfortAccount.confirmGuardianRevocation(friendAccount); // Notice this tries to confirm a non-existent revocation! + openfortAccount.confirmGuardianRevocation(friendAccount); + vm.prank(accountAdmin); vm.expectRevert(MustBeGuardian.selector); openfortAccount.revokeGuardian(friendAccount2); // Notice this tries to revoke a non-existent guardian! @@ -1908,8 +1920,10 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); openfortAccount.revokeGuardian(friendAccount); // Starting a valid revocation process skip(SECURITY_PERIOD + 1); + + // Try to confirm a guardian that is already valid and pending to revoke vm.expectRevert(DuplicatedGuardian.selector); - openfortAccount.confirmGuardianProposal(friendAccount); // Notice this tries to confirm a guardian that is already valid and pending to revoke! + openfortAccount.confirmGuardianProposal(friendAccount); } /** @@ -2036,7 +2050,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + // Using friendAccount2 first because it has a lower address + signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); skip(RECOVERY_PERIOD + 1); @@ -2108,7 +2123,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { /* * Case: User added 4 guardians and removes the default (Openfort) * One guardian (friend) is used to start a recovery process - * The guardian that initiatied the recovery + another one are used to complete the flow. + * The guardian that initiated the recovery + another one are used to complete the flow. * @notice Remember that signatures need to be ordered by the guardian's address. */ function test4GuardiansNoDefaultCompleteRecovery() public { From 73bab22f207b355e112753a3a3d72ab954f8fea2 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 10:08:53 +0100 Subject: [PATCH 30/83] Adding fuzz tests --- .../managed/ManagedOpenfortAccountTest.t.sol | 25 +++++++++++++++++++ .../UpgradeableOpenfortAccountTest.t.sol | 25 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index bef9807..0fcf0f1 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -95,6 +95,31 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(account2, managedOpenfortFactory.getAddressWithNonce(accountAdmin, "2")); } + /* + * Create an account by directly calling the factory by fuzzing the admin and nonce parameters. + */ + function testFuzzCreateAccountWithNonceViaFactory(address _adminAddress, bytes32 _nonce) public { + // Get the counterfactual address + vm.prank(factoryAdmin); + address account2 = managedOpenfortFactory.getAddressWithNonce(_adminAddress, _nonce); + + // Expect that we will see an event containing the account and admin + vm.expectEmit(true, true, false, true); + emit AccountCreated(account2, _adminAddress); + + // Deploy a managed account to the counterfactual address + vm.prank(factoryAdmin); + managedOpenfortFactory.createAccountWithNonce(_adminAddress, _nonce); + + // Calling it again should just return the address and not create another account + vm.prank(factoryAdmin); + managedOpenfortFactory.createAccountWithNonce(_adminAddress, _nonce); + + // Make sure the counterfactual address has not been altered + vm.prank(factoryAdmin); + assertEq(account2, managedOpenfortFactory.getAddressWithNonce(_adminAddress, _nonce)); + } + /* * Create an account calling the factory via EntryPoint. * Use initCode diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 131aa81..3eb21ba 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -100,6 +100,31 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(accountAddress2, upgradeableOpenfortFactory.getAddressWithNonce(accountAdmin, "2")); } + /* + * Create an account by directly calling the factory by fuzzing the admin and nonce parameters. + */ + function testFuzzCreateAccountWithNonceViaFactory(address _adminAddress, bytes32 _nonce) public { + // Get the counterfactual address + vm.prank(factoryAdmin); + address accountAddress2 = upgradeableOpenfortFactory.getAddressWithNonce(_adminAddress, _nonce); + + // Expect that we will see an event containing the account and admin + vm.expectEmit(true, true, false, true); + emit AccountCreated(accountAddress2, _adminAddress); + + // Deploy a upgradeable account to the counterfactual address + vm.prank(factoryAdmin); + upgradeableOpenfortFactory.createAccountWithNonce(_adminAddress, _nonce); + + // Calling it again should just return the address and not create another account + vm.prank(factoryAdmin); + upgradeableOpenfortFactory.createAccountWithNonce(_adminAddress, _nonce); + + // Make sure the counterfactual address has not been altered + vm.prank(factoryAdmin); + assertEq(accountAddress2, upgradeableOpenfortFactory.getAddressWithNonce(_adminAddress, _nonce)); + } + /* * Create an account calling the factory via EntryPoint. * Use initCode From cd53700bc1aa3028c67447ab449e9e3d03dc0fbd Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 13:31:06 +0100 Subject: [PATCH 31/83] Added IBaseOpenfortAccount interface file --- contracts/interfaces/IBaseOpenfortAccount.sol | 17 +++++++++++++++++ .../UpgradeableOpenfortAccountTest.t.sol | 5 +++-- 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 contracts/interfaces/IBaseOpenfortAccount.sol diff --git a/contracts/interfaces/IBaseOpenfortAccount.sol b/contracts/interfaces/IBaseOpenfortAccount.sol new file mode 100644 index 0000000..4eb7160 --- /dev/null +++ b/contracts/interfaces/IBaseOpenfortAccount.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {IAccount} from "account-abstraction/interfaces/IAccount.sol"; +import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; + +interface IBaseOpenfortAccount is IAccount { + function owner() external view returns (address); + + function getDeposit() 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/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 3eb21ba..6e73700 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -7,6 +7,7 @@ import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC20} from "contracts/mock/MockERC20.sol"; +import {IBaseOpenfortAccount} from "contracts/interfaces/IBaseOpenfortAccount.sol"; import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {OpenfortUpgradeableProxy} from "contracts/core/upgradeable/OpenfortUpgradeableProxy.sol"; @@ -963,7 +964,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testUpgradeAccount() public { assertEq(UpgradeableOpenfortAccount(payable(account)).owner(), address(accountAdmin)); - assertEq(address(UpgradeableOpenfortAccount(payable(account)).entryPoint()), address(entryPoint)); + assertEq(address(IBaseOpenfortAccount(payable(account)).entryPoint()), address(entryPoint)); MockV2UpgradeableOpenfortAccount newAccountImplementation = new MockV2UpgradeableOpenfortAccount(); OpenfortUpgradeableProxy p = OpenfortUpgradeableProxy(payable(account)); // Printing account address and the implementation address @@ -981,7 +982,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Notice that, even though we bind the address to the old implementation, entryPoint() is now changed assertEq( - address(UpgradeableOpenfortAccount(payable(account)).entryPoint()), + address(IBaseOpenfortAccount(payable(account)).entryPoint()), address(0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF) ); From 5451500ff751b9cc2d9dd50a6a64aacfce5dd8e7 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 14:48:19 +0100 Subject: [PATCH 32/83] Making OF_MSG_TYPEHASH internal --- contracts/core/base/BaseOpenfortAccount.sol | 166 ++++++++++---------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 809bd9d..6132f54 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -38,7 +38,7 @@ abstract contract BaseOpenfortAccount is // bytes4(keccak256("executeBatch(address[],uint256[],bytes[])") bytes4 internal constant EXECUTEBATCH_SELECTOR = 0x47e1da2a; // keccak256("OpenfortMessage(bytes32 hashedMessage)"); - bytes32 private constant OF_MSG_TYPEHASH = 0x57159f03b9efda178eab2037b2ec0b51ce11be0051b8a2a9992c29dc260e4a30; + bytes32 internal constant OF_MSG_TYPEHASH = 0x57159f03b9efda178eab2037b2ec0b51ce11be0051b8a2a9992c29dc260e4a30; /** * Struct like ValidationData (from the EIP-4337) - alpha solution - to keep track of session keys' data @@ -68,33 +68,40 @@ abstract contract BaseOpenfortAccount is _disableInitializers(); } - /** - * Require the function call went through EntryPoint or owner - */ - function _requireFromEntryPointOrOwner() internal view virtual { - if (msg.sender != address(entryPoint()) && msg.sender != owner()) { - revert NotOwnerOrEntrypoint(); - } - } - - /** - * Require the function call went through owner - */ - function _requireFromOwner() internal view { - if (msg.sender != owner()) { - revert NotOwner(); - } - } - function owner() public view virtual returns (address); /** * Check current account deposit in the entryPoint */ - function getDeposit() public view virtual returns (uint256) { + function getDeposit() external view virtual returns (uint256) { return entryPoint().balanceOf(address(this)); } + /* + * @notice See EIP-1271 + * Owner and session keys need to sign using EIP712. + */ + function isValidSignature(bytes32 _hash, bytes memory _signature) external view virtual override returns (bytes4) { + bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, _hash)); + bytes32 digest = _hashTypedDataV4(structHash); + address signer = digest.recover(_signature); + if (owner() == signer) return MAGICVALUE; + + SessionKeyStruct storage sessionKey = sessionKeys[signer]; + // If the signer is a session key that is still valid + if ( + sessionKey.validUntil == 0 || sessionKey.validAfter > block.timestamp + || sessionKey.validUntil < block.timestamp || sessionKey.limit < 1 + ) { + return 0xffffffff; + } // Not owner or session key revoked + else if (sessionKey.registrarAddress != owner()) { + return 0xffffffff; + } else { + return MAGICVALUE; + } + } + /* * @notice Return whether a sessionKey is valid. */ @@ -166,31 +173,6 @@ abstract contract BaseOpenfortAccount is return false; } - /* - * @notice See EIP-1271 - * Owner and session keys need to sign using EIP712. - */ - function isValidSignature(bytes32 _hash, bytes memory _signature) public view virtual override returns (bytes4) { - bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, _hash)); - bytes32 digest = _hashTypedDataV4(structHash); - address signer = digest.recover(_signature); - if (owner() == signer) return MAGICVALUE; - - SessionKeyStruct storage sessionKey = sessionKeys[signer]; - // If the signer is a session key that is still valid - if ( - sessionKey.validUntil == 0 || sessionKey.validAfter > block.timestamp - || sessionKey.validUntil < block.timestamp || sessionKey.limit < 1 - ) { - return 0xffffffff; - } // Not owner or session key revoked - else if (sessionKey.registrarAddress != owner()) { - return 0xffffffff; - } else { - return MAGICVALUE; - } - } - /** * Execute a transaction (called directly from owner, or by entryPoint) */ @@ -222,7 +204,7 @@ abstract contract BaseOpenfortAccount is /** * Deposit funds for this account in the EntryPoint */ - function addDeposit() public payable virtual { + function addDeposit() external payable virtual { entryPoint().depositTo{value: msg.value}(address(this)); } @@ -237,42 +219,6 @@ abstract contract BaseOpenfortAccount is entryPoint().withdrawTo(_withdrawAddress, _amount); } - /** - * @dev Call a target contract and reverts if it fails. - */ - function _call(address _target, uint256 _value, bytes calldata _calldata) internal virtual { - (bool success, bytes memory result) = _target.call{value: _value}(_calldata); - if (!success) { - assembly { - revert(add(result, 32), mload(result)) - } - } - } - - /** - * @inheritdoc BaseAccount - */ - function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash) - internal - virtual - override - returns (uint256 validationData) - { - require(entryPoint().getUserOpHash(userOp) == userOpHash, "Calculated userOpHash doesn't match"); - bytes32 hash = userOpHash.toEthSignedMessageHash(); - address signer = hash.recover(userOp.signature); - - // If the userOp was signed by the owner, allow straightaway - if (owner() == signer) return 0; - - // Check if the session key is valid according to the data in the userOp - if (isValidSessionKey(signer, userOp.callData)) { - return _packValidationData(false, sessionKeys[signer].validUntil, sessionKeys[signer].validAfter); - } - - return SIG_VALIDATION_FAILED; - } - /** * Register a session key to the account * @param _key session key to register @@ -287,7 +233,7 @@ abstract contract BaseOpenfortAccount is uint48 _validUntil, uint48 _limit, address[] calldata _whitelist - ) public virtual { + ) external virtual { _requireFromEntryPointOrOwner(); require(_whitelist.length < 11, "Whitelist too big"); @@ -328,4 +274,58 @@ abstract contract BaseOpenfortAccount is emit SessionKeyRevoked(_key); } } + + /** + * @dev Call a target contract and reverts if it fails. + */ + function _call(address _target, uint256 _value, bytes calldata _calldata) internal virtual { + (bool success, bytes memory result) = _target.call{value: _value}(_calldata); + if (!success) { + assembly { + revert(add(result, 32), mload(result)) + } + } + } + + /** + * @inheritdoc BaseAccount + */ + function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash) + internal + virtual + override + returns (uint256 validationData) + { + require(entryPoint().getUserOpHash(userOp) == userOpHash, "Calculated userOpHash doesn't match"); + bytes32 hash = userOpHash.toEthSignedMessageHash(); + address signer = hash.recover(userOp.signature); + + // If the userOp was signed by the owner, allow straightaway + if (owner() == signer) return 0; + + // Check if the session key is valid according to the data in the userOp + if (isValidSessionKey(signer, userOp.callData)) { + return _packValidationData(false, sessionKeys[signer].validUntil, sessionKeys[signer].validAfter); + } + + return SIG_VALIDATION_FAILED; + } + + /** + * Require the function call went through EntryPoint or owner + */ + function _requireFromEntryPointOrOwner() internal view virtual { + if (msg.sender != address(entryPoint()) && msg.sender != owner()) { + revert NotOwnerOrEntrypoint(); + } + } + + /** + * Require the function call went through owner + */ + function _requireFromOwner() internal view { + if (msg.sender != owner()) { + revert NotOwner(); + } + } } From 2a2596f9b73530192372bf8e327c23d7dfdc8ca6 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 15:04:32 +0100 Subject: [PATCH 33/83] Moved BaseOpenfortFactory logic, better modularity --- contracts/core/base/BaseOpenfortFactory.sol | 33 +++++++++- .../core/managed/ManagedOpenfortFactory.sol | 63 ++++++++++++------- .../UpgradeableOpenfortFactory.sol | 34 ++-------- 3 files changed, 77 insertions(+), 53 deletions(-) diff --git a/contracts/core/base/BaseOpenfortFactory.sol b/contracts/core/base/BaseOpenfortFactory.sol index 0c849cf..6ae44ca 100644 --- a/contracts/core/base/BaseOpenfortFactory.sol +++ b/contracts/core/base/BaseOpenfortFactory.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; +import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; /** @@ -9,7 +12,7 @@ import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; * It inherits from: * - IBaseOpenfortFactory */ -abstract contract BaseOpenfortFactory is IBaseOpenfortFactory { +abstract contract BaseOpenfortFactory is IBaseOpenfortFactory, Ownable2StepUpgradeable { address public entrypointContract; address public accountImplementation; uint256 public recoveryPeriod; @@ -21,6 +24,7 @@ abstract contract BaseOpenfortFactory is IBaseOpenfortFactory { error InsecurePeriod(); constructor( + address _owner, address _entrypoint, address _accountImplementation, uint256 _recoveryPeriod, @@ -29,12 +33,16 @@ abstract contract BaseOpenfortFactory is IBaseOpenfortFactory { uint256 _lockPeriod, address _openfortGuardian ) { - if (_entrypoint == address(0) || _accountImplementation == address(0) || _openfortGuardian == address(0)) { + if ( + _owner == address(0) || _entrypoint == address(0) || _accountImplementation == address(0) + || _openfortGuardian == address(0) + ) { revert ZeroAddressNotAllowed(); } if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { revert InsecurePeriod(); } + _transferOwnership(_owner); entrypointContract = _entrypoint; accountImplementation = _accountImplementation; recoveryPeriod = _recoveryPeriod; @@ -43,4 +51,25 @@ abstract contract BaseOpenfortFactory is IBaseOpenfortFactory { lockPeriod = _lockPeriod; openfortGuardian = _openfortGuardian; } + + /** + * @dev {See IBaseOpenfortFactory} + */ + function addStake(uint32 unstakeDelaySec) external payable onlyOwner { + IEntryPoint(entrypointContract).addStake{value: msg.value}(unstakeDelaySec); + } + + /** + * @dev {See IBaseOpenfortFactory} + */ + function unlockStake() external onlyOwner { + IEntryPoint(entrypointContract).unlockStake(); + } + + /** + * @dev {See IBaseOpenfortFactory} + */ + function withdrawStake(address payable withdrawAddress) external onlyOwner { + IEntryPoint(entrypointContract).withdrawStake(withdrawAddress); + } } diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index 98709b2..5f58c1a 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -2,21 +2,30 @@ pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import {ManagedOpenfortAccount} from "./ManagedOpenfortAccount.sol"; import {OpenfortManagedProxy} from "./OpenfortManagedProxy.sol"; -import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; -import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; +import {BaseOpenfortFactory, AddressUpgradeable} from "../base/BaseOpenfortFactory.sol"; /** * @title ManagedOpenfortFactory (Non-upgradeable) * @notice Contract to create an on-chain factory to deploy new ManagedOpenfortAccounts. - * It uses OpenZeppelin's Create2 and OpenfortManagedProxy libraries. * It inherits from: - * - IBaseOpenfortFactory - * - UpgradeableBeacon to also work as the beacon + * - BaseOpenfortFactory + * - IBeacon to work as the beacon */ -contract ManagedOpenfortFactory is BaseOpenfortFactory, UpgradeableBeacon { +contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { + address private _implementation; + + /** + * @dev Emitted when the implementation returned by the beacon is changed. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the + * beacon. + */ constructor( address _owner, address _entrypoint, @@ -28,6 +37,7 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, UpgradeableBeacon { address _openfortGuardian ) BaseOpenfortFactory( + _owner, _entrypoint, _accountImplementation, _recoveryPeriod, @@ -36,12 +46,8 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, UpgradeableBeacon { _lockPeriod, _openfortGuardian ) - UpgradeableBeacon(_accountImplementation) { - if (_owner == address(0)) { - revert ZeroAddressNotAllowed(); - } - _transferOwnership(_owner); + _setImplementation(_accountImplementation); } /* @@ -72,23 +78,38 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, UpgradeableBeacon { } /** - * @dev {See BaseOpenfortFactory} + * @dev Returns the current implementation address. */ - function addStake(uint32 unstakeDelaySec) external payable onlyOwner { - IEntryPoint(entrypointContract).addStake{value: msg.value}(unstakeDelaySec); + function implementation() public view virtual override returns (address) { + return _implementation; } /** - * @dev {See BaseOpenfortFactory} + * @dev Upgrades the beacon to a new implementation. + * + * Emits an {Upgraded} event. + * + * Requirements: + * + * - msg.sender must be the owner of the contract. + * - `newImplementation` must be a contract. */ - function unlockStake() external onlyOwner { - IEntryPoint(entrypointContract).unlockStake(); + function upgradeTo(address newImplementation) public virtual onlyOwner { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); } /** - * @dev {See BaseOpenfortFactory} + * @dev Sets the implementation contract address for this beacon + * + * Requirements: + * + * - `newImplementation` must be a contract. */ - function withdrawStake(address payable withdrawAddress) external onlyOwner { - IEntryPoint(entrypointContract).withdrawStake(withdrawAddress); + function _setImplementation(address newImplementation) private { + require( + AddressUpgradeable.isContract(newImplementation), "ManagedOpenfortFactory: implementation is not a contract" + ); + _implementation = newImplementation; } } diff --git a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol index f8f4dee..2ab291e 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol @@ -2,7 +2,6 @@ pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {UpgradeableOpenfortAccount, IEntryPoint} from "./UpgradeableOpenfortAccount.sol"; import {OpenfortUpgradeableProxy} from "./OpenfortUpgradeableProxy.sol"; import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; @@ -13,9 +12,8 @@ import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; * It uses OpenZeppelin's Create2 and OpenfortUpgradeableProxy libraries. * It inherits from: * - BaseOpenfortFactory - * - Ownable2StepUpgradeable */ -contract UpgradeableOpenfortFactory is BaseOpenfortFactory, Ownable2StepUpgradeable { +contract UpgradeableOpenfortFactory is BaseOpenfortFactory { constructor( address _owner, address _entrypoint, @@ -27,6 +25,7 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory, Ownable2StepUpgradea address _openfortGuardian ) BaseOpenfortFactory( + _owner, _entrypoint, _accountImplementation, _recoveryPeriod, @@ -35,9 +34,7 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory, Ownable2StepUpgradea _lockPeriod, _openfortGuardian ) - { - _transferOwnership(_owner); - } + {} /* * @notice Deploy a new account for _admin with a nonce. @@ -46,9 +43,7 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory, Ownable2StepUpgradea bytes32 salt = keccak256(abi.encode(_admin, _nonce)); account = getAddressWithNonce(_admin, _nonce); - if (account.code.length > 0) { - return account; - } + if (account.code.length > 0) return account; emit AccountCreated(account, _admin); account = address(new OpenfortUpgradeableProxy{salt: salt}(accountImplementation, "")); @@ -69,25 +64,4 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory, Ownable2StepUpgradea ) ); } - - /** - * @dev {See BaseOpenfortFactory} - */ - function addStake(uint32 unstakeDelaySec) external payable onlyOwner { - IEntryPoint(entrypointContract).addStake{value: msg.value}(unstakeDelaySec); - } - - /** - * @dev {See BaseOpenfortFactory} - */ - function unlockStake() external onlyOwner { - IEntryPoint(entrypointContract).unlockStake(); - } - - /** - * @dev {See BaseOpenfortFactory} - */ - function withdrawStake(address payable withdrawAddress) external onlyOwner { - IEntryPoint(entrypointContract).withdrawStake(withdrawAddress); - } } From 50a6eca4bae62f04c0058d50da43987f0e040007 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 15:39:34 +0100 Subject: [PATCH 34/83] restructuring namings --- contracts/core/base/BaseOpenfortFactory.sol | 18 ++++++++++++----- .../core/managed/ManagedOpenfortFactory.sol | 20 ++++++------------- ...agedProxy.sol => ManagedOpenfortProxy.sol} | 6 +++--- .../UpgradeableOpenfortFactory.sol | 10 ++++------ ...Proxy.sol => UpgradeableOpenfortProxy.sol} | 4 ++-- contracts/interfaces/IBaseOpenfortFactory.sol | 2 +- .../managed/ManagedOpenfortAccountTest.t.sol | 2 +- .../UpgradeableOpenfortAccountTest.t.sol | 4 ++-- 8 files changed, 32 insertions(+), 34 deletions(-) rename contracts/core/managed/{OpenfortManagedProxy.sol => ManagedOpenfortProxy.sol} (67%) rename contracts/core/upgradeable/{OpenfortUpgradeableProxy.sol => UpgradeableOpenfortProxy.sol} (80%) diff --git a/contracts/core/base/BaseOpenfortFactory.sol b/contracts/core/base/BaseOpenfortFactory.sol index 6ae44ca..5ff0586 100644 --- a/contracts/core/base/BaseOpenfortFactory.sol +++ b/contracts/core/base/BaseOpenfortFactory.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; +import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; @@ -11,10 +11,11 @@ import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; * @notice Contract to create an on-chain factory to deploy new OpenfortAccounts. * It inherits from: * - IBaseOpenfortFactory + * - Ownable2Step */ -abstract contract BaseOpenfortFactory is IBaseOpenfortFactory, Ownable2StepUpgradeable { +abstract contract BaseOpenfortFactory is IBaseOpenfortFactory, Ownable2Step { address public entrypointContract; - address public accountImplementation; + address internal _implementation; uint256 public recoveryPeriod; uint256 public securityPeriod; uint256 public securityWindow; @@ -44,7 +45,7 @@ abstract contract BaseOpenfortFactory is IBaseOpenfortFactory, Ownable2StepUpgra } _transferOwnership(_owner); entrypointContract = _entrypoint; - accountImplementation = _accountImplementation; + _implementation = _accountImplementation; recoveryPeriod = _recoveryPeriod; securityPeriod = _securityPeriod; securityWindow = _securityWindow; @@ -52,6 +53,13 @@ abstract contract BaseOpenfortFactory is IBaseOpenfortFactory, Ownable2StepUpgra openfortGuardian = _openfortGuardian; } + /** + * @dev Returns the current implementation address. + */ + function implementation() external view virtual override returns (address) { + return _implementation; + } + /** * @dev {See IBaseOpenfortFactory} */ diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index 5f58c1a..11427b2 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -4,8 +4,8 @@ pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import {ManagedOpenfortAccount} from "./ManagedOpenfortAccount.sol"; -import {OpenfortManagedProxy} from "./OpenfortManagedProxy.sol"; -import {BaseOpenfortFactory, AddressUpgradeable} from "../base/BaseOpenfortFactory.sol"; +import {ManagedOpenfortProxy} from "./ManagedOpenfortProxy.sol"; +import {BaseOpenfortFactory, Address} from "../base/BaseOpenfortFactory.sol"; /** * @title ManagedOpenfortFactory (Non-upgradeable) @@ -15,17 +15,11 @@ import {BaseOpenfortFactory, AddressUpgradeable} from "../base/BaseOpenfortFacto * - IBeacon to work as the beacon */ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { - address private _implementation; - /** * @dev Emitted when the implementation returned by the beacon is changed. */ event Upgraded(address indexed implementation); - /** - * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the - * beacon. - */ constructor( address _owner, address _entrypoint, @@ -61,7 +55,7 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { emit AccountCreated(account, _admin); - account = address(new OpenfortManagedProxy{salt: salt}(address(this), "")); + account = address(new ManagedOpenfortProxy{salt: salt}(address(this), "")); ManagedOpenfortAccount(payable(account)).initialize( _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, openfortGuardian ); @@ -73,14 +67,14 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { function getAddressWithNonce(address _admin, bytes32 _nonce) public view returns (address) { bytes32 salt = keccak256(abi.encode(_admin, _nonce)); return Create2.computeAddress( - salt, keccak256(abi.encodePacked(type(OpenfortManagedProxy).creationCode, abi.encode(address(this), ""))) + salt, keccak256(abi.encodePacked(type(ManagedOpenfortProxy).creationCode, abi.encode(address(this), ""))) ); } /** * @dev Returns the current implementation address. */ - function implementation() public view virtual override returns (address) { + function implementation() public view virtual override(BaseOpenfortFactory, IBeacon) returns (address) { return _implementation; } @@ -107,9 +101,7 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { * - `newImplementation` must be a contract. */ function _setImplementation(address newImplementation) private { - require( - AddressUpgradeable.isContract(newImplementation), "ManagedOpenfortFactory: implementation is not a contract" - ); + require(Address.isContract(newImplementation), "ManagedOpenfortFactory: implementation is not a contract"); _implementation = newImplementation; } } diff --git a/contracts/core/managed/OpenfortManagedProxy.sol b/contracts/core/managed/ManagedOpenfortProxy.sol similarity index 67% rename from contracts/core/managed/OpenfortManagedProxy.sol rename to contracts/core/managed/ManagedOpenfortProxy.sol index 7d835e5..2e1df4f 100644 --- a/contracts/core/managed/OpenfortManagedProxy.sol +++ b/contracts/core/managed/ManagedOpenfortProxy.sol @@ -4,12 +4,12 @@ pragma solidity =0.8.19; import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; /** - * @title OpenfortManagedProxy (Non-upgradeable) - * @notice Contract to create the beacon. It determines the implementation contract. + * @title ManagedOpenfortProxy (Non-upgradeable) + * @notice Contract to create a beacon proxy. It determines the implementation contract. * It inherits from: * - BeaconProxy */ -contract OpenfortManagedProxy is BeaconProxy { +contract ManagedOpenfortProxy is BeaconProxy { constructor(address beacon, bytes memory data) BeaconProxy(beacon, data) {} function implementation() external view returns (address) { diff --git a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol index 2ab291e..3f0944d 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol @@ -3,13 +3,13 @@ pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {UpgradeableOpenfortAccount, IEntryPoint} from "./UpgradeableOpenfortAccount.sol"; -import {OpenfortUpgradeableProxy} from "./OpenfortUpgradeableProxy.sol"; +import {UpgradeableOpenfortProxy} from "./UpgradeableOpenfortProxy.sol"; import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; /** * @title UpgradeableOpenfortFactory (Non-upgradeable) * @notice Contract to create an on-chain factory to deploy new UpgradeableOpenfortAccounts. - * It uses OpenZeppelin's Create2 and OpenfortUpgradeableProxy libraries. + * It uses OpenZeppelin's Create2 and UpgradeableOpenfortProxy libraries. * It inherits from: * - BaseOpenfortFactory */ @@ -46,7 +46,7 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { if (account.code.length > 0) return account; emit AccountCreated(account, _admin); - account = address(new OpenfortUpgradeableProxy{salt: salt}(accountImplementation, "")); + account = address(new UpgradeableOpenfortProxy{salt: salt}(_implementation, "")); UpgradeableOpenfortAccount(payable(account)).initialize( _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, openfortGuardian ); @@ -59,9 +59,7 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { bytes32 salt = keccak256(abi.encode(_admin, _nonce)); return Create2.computeAddress( salt, - keccak256( - abi.encodePacked(type(OpenfortUpgradeableProxy).creationCode, abi.encode(accountImplementation, "")) - ) + keccak256(abi.encodePacked(type(UpgradeableOpenfortProxy).creationCode, abi.encode(_implementation, ""))) ); } } diff --git a/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol b/contracts/core/upgradeable/UpgradeableOpenfortProxy.sol similarity index 80% rename from contracts/core/upgradeable/OpenfortUpgradeableProxy.sol rename to contracts/core/upgradeable/UpgradeableOpenfortProxy.sol index 06628ca..15ec38b 100644 --- a/contracts/core/upgradeable/OpenfortUpgradeableProxy.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortProxy.sol @@ -4,12 +4,12 @@ pragma solidity =0.8.19; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; /** - * @title OpenfortUpgradeableProxy (Non-upgradeable) + * @title UpgradeableOpenfortProxy (Non-upgradeable) * @notice Contract to create the proxies * It inherits from: * - ERC1967Proxy */ -contract OpenfortUpgradeableProxy is ERC1967Proxy { +contract UpgradeableOpenfortProxy is ERC1967Proxy { constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) {} function implementation() external view returns (address) { diff --git a/contracts/interfaces/IBaseOpenfortFactory.sol b/contracts/interfaces/IBaseOpenfortFactory.sol index fb8278c..05ab6ad 100644 --- a/contracts/interfaces/IBaseOpenfortFactory.sol +++ b/contracts/interfaces/IBaseOpenfortFactory.sol @@ -12,7 +12,7 @@ interface IBaseOpenfortFactory { function createAccountWithNonce(address _admin, bytes32 _nonce) external returns (address account); /// @notice Returns the address of the Account implementation. - function accountImplementation() external view returns (address); + function implementation() external view returns (address); /// @notice Returns the address of an Account that would be deployed with the given admin and nonce. function getAddressWithNonce(address _admin, bytes32 _nonce) external view returns (address); diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 0fcf0f1..cdab6ec 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -9,7 +9,7 @@ import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC20} from "contracts/mock/MockERC20.sol"; import {ManagedOpenfortAccount} from "contracts/core/managed/ManagedOpenfortAccount.sol"; import {ManagedOpenfortFactory} from "contracts/core/managed/ManagedOpenfortFactory.sol"; -import {OpenfortManagedProxy} from "contracts/core/managed/OpenfortManagedProxy.sol"; +import {ManagedOpenfortProxy} from "contracts/core/managed/ManagedOpenfortProxy.sol"; import {MockV2ManagedOpenfortAccount} from "contracts/mock/MockV2ManagedOpenfortAccount.sol"; import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 6e73700..8f0f215 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -10,7 +10,7 @@ import {MockERC20} from "contracts/mock/MockERC20.sol"; import {IBaseOpenfortAccount} from "contracts/interfaces/IBaseOpenfortAccount.sol"; import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; -import {OpenfortUpgradeableProxy} from "contracts/core/upgradeable/OpenfortUpgradeableProxy.sol"; +import {UpgradeableOpenfortProxy} from "contracts/core/upgradeable/UpgradeableOpenfortProxy.sol"; import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; @@ -966,7 +966,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(UpgradeableOpenfortAccount(payable(account)).owner(), address(accountAdmin)); assertEq(address(IBaseOpenfortAccount(payable(account)).entryPoint()), address(entryPoint)); MockV2UpgradeableOpenfortAccount newAccountImplementation = new MockV2UpgradeableOpenfortAccount(); - OpenfortUpgradeableProxy p = OpenfortUpgradeableProxy(payable(account)); + UpgradeableOpenfortProxy p = UpgradeableOpenfortProxy(payable(account)); // Printing account address and the implementation address console.log("Account address (proxy): ", account); console.log("Implementation address (old): ", p.implementation()); From 61da68bccd7c0ed6512f4b08633f4b867a5f03d2 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 16:18:30 +0100 Subject: [PATCH 35/83] Improving the tests --- contracts/core/base/BaseOpenfortAccount.sol | 2 +- .../managed/ManagedOpenfortAccountTest.t.sol | 26 +++++++++++++++---- .../UpgradeableOpenfortAccountTest.t.sol | 24 ++++++++++++++--- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 6132f54..9369e54 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -71,7 +71,7 @@ abstract contract BaseOpenfortAccount is function owner() public view virtual returns (address); /** - * Check current account deposit in the entryPoint + * Check current account deposit in the EntryPoint */ function getDeposit() external view virtual returns (uint256) { return entryPoint().balanceOf(address(this)); diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index cdab6ec..f8cbdb6 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -16,13 +16,13 @@ import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; contract ManagedOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; - ManagedOpenfortAccount public managedOpenfortAccount; + ManagedOpenfortAccount public managedOpenfortAccountImpl; ManagedOpenfortFactory public managedOpenfortFactory; /** * @notice Initialize the ManagedOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the managedOpenfortAccount and managedOpenfortFactory/Beacon + * - factoryAdmin is the deployer (and owner) of the managedOpenfortAccountImpl and managedOpenfortFactory/Beacon * - accountAdmin is the account used to deploy new managed accounts using the factory * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps @@ -49,12 +49,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { entryPoint = EntryPoint(payable(targetAddr)); } // deploy account implementation - managedOpenfortAccount = new ManagedOpenfortAccount{salt: versionSalt}(); + managedOpenfortAccountImpl = new ManagedOpenfortAccount{salt: versionSalt}(); // deploy account factory (beacon) managedOpenfortFactory = new ManagedOpenfortFactory{salt: versionSalt}( factoryAdmin, address(entryPoint), - address(managedOpenfortAccount), + address(managedOpenfortAccountImpl), RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, @@ -70,6 +70,22 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.stopPrank(); } + /* + * Should not be able to initialize the implementation + */ + function testInitializeImplementation() public { + vm.expectRevert("Initializable: contract is already initialized"); + managedOpenfortAccountImpl.initialize( + accountAdmin, + address(entryPoint), + RECOVERY_PERIOD, + SECURITY_PERIOD, + SECURITY_WINDOW, + LOCK_PERIOD, + OPENFORT_GUARDIAN + ); + } + /* * Create an account by directly calling the factory. */ @@ -957,7 +973,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address newEntryPoint = 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF; // Check addressess - assertEq(address(managedOpenfortAccount.entryPoint()), address(entryPoint)); + assertEq(address(managedOpenfortAccountImpl.entryPoint()), address(entryPoint)); // Try to use the old and new implementation before upgrade (should always behave with current values) assertEq(MockV2ManagedOpenfortAccount(payable(account)).getLock(), 0); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 8f0f215..61f14fe 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -17,7 +17,7 @@ import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; - UpgradeableOpenfortAccount public upgradeableOpenfortAccount; + UpgradeableOpenfortAccount public upgradeableOpenfortAccountImpl; UpgradeableOpenfortFactory public upgradeableOpenfortFactory; /** @@ -53,12 +53,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // deploy upgradeable account implementation vm.expectEmit(true, true, false, true); emit AccountImplementationDeployed(factoryAdmin); - upgradeableOpenfortAccount = new UpgradeableOpenfortAccount{salt: versionSalt}(); + upgradeableOpenfortAccountImpl = new UpgradeableOpenfortAccount{salt: versionSalt}(); // deploy upgradeable account factory upgradeableOpenfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( factoryAdmin, address(entryPoint), - address(upgradeableOpenfortAccount), + address(upgradeableOpenfortAccountImpl), RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, @@ -76,6 +76,22 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.stopPrank(); } + /* + * Should not be able to initialize the implementation + */ + function testInitializeImplementation() public { + vm.expectRevert("Initializable: contract is already initialized"); + upgradeableOpenfortAccountImpl.initialize( + accountAdmin, + address(entryPoint), + RECOVERY_PERIOD, + SECURITY_PERIOD, + SECURITY_WINDOW, + LOCK_PERIOD, + OPENFORT_GUARDIAN + ); + } + /* * Create an account by directly calling the factory. */ @@ -1003,7 +1019,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UpgradeableOpenfortFactory upgradeableOpenfortFactoryOld = new UpgradeableOpenfortFactory{salt: versionSalt}( factoryAdmin, oldEntryPoint, - address(upgradeableOpenfortAccount), + address(upgradeableOpenfortAccountImpl), RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, From 63cf512ad6cac5d597ac7e7f6372964e287773f9 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 16:51:33 +0100 Subject: [PATCH 36/83] Improving test namings --- contracts/interfaces/IBaseOpenfortAccount.sol | 73 +++- .../interfaces/IBaseRecoverableAccount.sol | 66 ++++ test/foundry/core/OpenfortBaseTest.t.sol | 2 +- .../managed/ManagedOpenfortAccountTest.t.sol | 366 ++++++++--------- .../UpgradeableOpenfortAccountTest.t.sol | 374 +++++++++--------- .../paymaster/OpenfortPaymasterV2Test.t.sol | 163 ++++---- 6 files changed, 590 insertions(+), 454 deletions(-) create mode 100644 contracts/interfaces/IBaseRecoverableAccount.sol diff --git a/contracts/interfaces/IBaseOpenfortAccount.sol b/contracts/interfaces/IBaseOpenfortAccount.sol index 4eb7160..5ed838d 100644 --- a/contracts/interfaces/IBaseOpenfortAccount.sol +++ b/contracts/interfaces/IBaseOpenfortAccount.sol @@ -2,16 +2,73 @@ pragma solidity =0.8.19; import {IAccount} from "account-abstraction/interfaces/IAccount.sol"; -import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; interface IBaseOpenfortAccount is IAccount { - function owner() external view returns (address); + error InsufficientBalance(uint256 amountRequired, uint256 currentBalance); + error InvalidParameterLength(); + error MustSendNativeToken(); + error NotOwner(); + error NotOwnerOrEntrypoint(); + error OwnerNotAllowed(); + error ZeroAddressNotAllowed(); + error ZeroValueNotAllowed(); - function getDeposit() external view returns (uint256); + event AccountImplementationDeployed(address indexed creator); + event EIP712DomainChanged(); + event EntryPointUpdated(address oldEntryPoint, address newEntryPoint); + event Initialized(uint8 version); + event SessionKeyRegistered(address indexed key); + event SessionKeyRevoked(address indexed key); + + receive() external payable; - /** - * return the entryPoint used by this account. - * subclass should return the current entryPoint used by this account. - */ - function entryPoint() external view returns (IEntryPoint); + function addDeposit() external payable; + function eip712Domain() + external + view + returns ( + bytes1 fields, + string memory name, + string memory version, + uint256 chainId, + address verifyingContract, + bytes32 salt, + uint256[] memory extensions + ); + function entryPoint() external view returns (address); + function execute(address dest, uint256 value, bytes memory func) external; + function executeBatch(address[] memory _target, uint256[] memory _value, bytes[] memory _calldata) external; + function getDeposit() external view returns (uint256); + function getNonce() external view returns (uint256); + function isValidSessionKey(address _sessionKey, bytes memory _callData) external returns (bool); + function isValidSignature(bytes32 _hash, bytes memory _signature) external view returns (bytes4); + function onERC1155BatchReceived(address, address, uint256[] memory, uint256[] memory, bytes memory) + external + pure + returns (bytes4); + function onERC1155Received(address, address, uint256, uint256, bytes memory) external pure returns (bytes4); + function onERC721Received(address, address, uint256, bytes memory) external view returns (bytes4); + function owner() external view returns (address); + function registerSessionKey( + address _key, + uint48 _validAfter, + uint48 _validUntil, + uint48 _limit, + address[] memory _whitelist + ) external; + function revokeSessionKey(address _key) external; + function sessionKeys(address sessionKey) + external + view + returns ( + uint48 validAfter, + uint48 validUntil, + uint48 limit, + bool masterSessionKey, + bool whitelisting, + address registrarAddress + ); + function supportsInterface(bytes4 interfaceId) external view returns (bool); + function tokensReceived(address, address, address, uint256, bytes memory, bytes memory) external pure; + function withdrawDepositTo(address payable _withdrawAddress, uint256 _amount) external; } diff --git a/contracts/interfaces/IBaseRecoverableAccount.sol b/contracts/interfaces/IBaseRecoverableAccount.sol new file mode 100644 index 0000000..cc8dcca --- /dev/null +++ b/contracts/interfaces/IBaseRecoverableAccount.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {IBaseOpenfortAccount} from "./IBaseOpenfortAccount.sol"; + +interface IBaseRecoverableAccount is IBaseOpenfortAccount { + error AccountLocked(); + error AccountNotLocked(); + error CannotUnlock(); + error DuplicatedGuardian(); + error DuplicatedProposal(); + error DuplicatedRevoke(); + error GuardianCannotBeOwner(); + error InsecurePeriod(); + error InvalidRecoverySignatures(); + error InvalidSignatureAmount(); + error MustBeGuardian(); + error NoOngoingRecovery(); + error OngoingRecovery(); + error PendingProposalExpired(); + error PendingProposalNotOver(); + error PendingRevokeExpired(); + error PendingRevokeNotOver(); + error UnknownProposal(); + error UnknownRevoke(); + + event GuardianAdded(address indexed guardian); + event GuardianProposalCancelled(address indexed guardian); + event GuardianProposed(address indexed guardian, uint256 executeAfter); + event GuardianRevocationCancelled(address indexed guardian); + event GuardianRevocationRequested(address indexed guardian, uint256 executeAfter); + event GuardianRevoked(address indexed guardian); + event Locked(bool isLocked); + event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + event RecoveryCancelled(address indexed recoveryAddress); + event RecoveryCompleted(address indexed recoveryAddress); + event RecoveryExecuted(address indexed recoveryAddress, uint64 executeAfter); + + receive() external payable; + + function acceptOwnership() external; + function cancelGuardianProposal(address _guardian) external; + function cancelGuardianRevocation(address _guardian) external; + function cancelRecovery() external; + function completeRecovery(bytes[] memory _signatures) external; + function confirmGuardianProposal(address _guardian) external; + function confirmGuardianRevocation(address _guardian) external; + function getGuardians() external view returns (address[] memory); + function getLock() external view returns (uint256 _releaseAfter); + function guardianCount() external view returns (uint256); + function isGuardian(address _guardian) external view returns (bool); + function isGuardianOrGuardianSigner(address _guardian) external pure returns (bool _isGuardian); + function isLocked() external view returns (bool); + function lock() external; + function proposeGuardian(address _guardian) external; + function recoveryDetails() + external + view + returns (address recoveryAddress, uint64 executeAfter, uint32 guardiansRequired); + function renounceOwnership() external; + function revokeGuardian(address _guardian) external; + function startRecovery(address _recoveryAddress) external; + function transferOwnership(address _newOwner) external; + function unlock() external; +} diff --git a/test/foundry/core/OpenfortBaseTest.t.sol b/test/foundry/core/OpenfortBaseTest.t.sol index 544bbc1..9dc0a73 100644 --- a/test/foundry/core/OpenfortBaseTest.t.sol +++ b/test/foundry/core/OpenfortBaseTest.t.sol @@ -16,7 +16,7 @@ contract OpenfortBaseTest is Test { EntryPoint public entryPoint; - address public account; + address public accountAddress; TestCounter public testCounter; MockERC20 public mockERC20; diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index f8cbdb6..988a05a 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -62,7 +62,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { OPENFORT_GUARDIAN ); // Create an managed account wallet and get its address - account = managedOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); + accountAddress = managedOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); // deploy a new TestCounter testCounter = new TestCounter{salt: versionSalt}(); // deploy a new MockERC20 (ERC20) @@ -176,14 +176,16 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterDirect() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the managed account wallet (deployer) call "count" vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).execute(address(testCounter), 0, abi.encodeWithSignature("count()")); + ManagedOpenfortAccount(payable(accountAddress)).execute( + address(testCounter), 0, abi.encodeWithSignature("count()") + ); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -192,19 +194,19 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaEntrypoint() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -213,7 +215,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaEntrypointBatching() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); uint256 count = 3; address[] memory targets = new address[](count); @@ -227,15 +229,15 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, accountAdminPKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 3); + assertEq(testCounter.counters(accountAddress), 3); } /* @@ -243,23 +245,23 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyNotregistered() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -267,7 +269,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaSessionKey() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -275,19 +277,21 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey( + sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist + ); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -296,7 +300,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testRegisterSessionKeyViaEntrypoint() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -304,7 +308,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOp( - account, + accountAddress, accountAdminPKey, bytes(""), abi.encodeWithSignature( @@ -317,25 +321,25 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -345,7 +349,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -353,7 +357,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOp( - account, + accountAddress, accountAdminPKey, bytes(""), abi.encodeWithSignature( @@ -366,32 +370,32 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); address sessionKeyAttack; uint256 sessionKeyPrivKeyAttack; (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); userOp = _setupUserOp( - account, + accountAddress, sessionKeyPrivKey, bytes(""), abi.encodeWithSignature( @@ -404,7 +408,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -415,7 +419,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaSessionKeyExpired() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -424,20 +428,20 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.warp(100); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); vm.expectRevert(); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -445,7 +449,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyRevoked() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -453,20 +457,20 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); - ManagedOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); + ManagedOpenfortAccount(payable(accountAddress)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -474,7 +478,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyReachLimit() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -484,28 +488,28 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has only increased by one - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -513,7 +517,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -523,7 +527,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); uint256 count = 3; address[] memory targets = new address[](count); @@ -537,15 +541,15 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -553,7 +557,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailRevokeSessionKeyInvalidUser() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -561,21 +565,21 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); vm.prank(beneficiary); - ManagedOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); + ManagedOpenfortAccount(payable(accountAddress)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -583,7 +587,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaSessionKeyWhitelisting() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -592,19 +596,19 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -612,7 +616,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -620,19 +624,19 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](11); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -640,7 +644,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaSessionKeyWhitelistingBatch() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -649,12 +653,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); // Verify that the registered key is not a MasterKey but has whitelisting bool isMasterKey; bool isWhitelisted; - (,,, isMasterKey, isWhitelisted,) = ManagedOpenfortAccount(payable(account)).sessionKeys(sessionKey); + (,,, isMasterKey, isWhitelisted,) = ManagedOpenfortAccount(payable(accountAddress)).sessionKeys(sessionKey); assert(!isMasterKey); assert(isWhitelisted); @@ -670,15 +674,15 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 3); + assertEq(testCounter.counters(accountAddress), 3); } /* @@ -686,7 +690,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyWhitelistingBatch() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -695,12 +699,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); // Verify that the registered key is not a MasterKey but has whitelisting bool isMasterKey; bool isWhitelisted; - (,,, isMasterKey, isWhitelisted,) = ManagedOpenfortAccount(payable(account)).sessionKeys(sessionKey); + (,,, isMasterKey, isWhitelisted,) = ManagedOpenfortAccount(payable(accountAddress)).sessionKeys(sessionKey); assert(!isMasterKey); assert(isWhitelisted); @@ -716,15 +720,15 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -732,28 +736,28 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory whitelist = new address[](1); - whitelist[0] = address(account); + whitelist[0] = address(accountAddress); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -761,16 +765,16 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory whitelist = new address[](1); - whitelist[0] = address(account); + whitelist[0] = address(accountAddress); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); uint256 count = 3; address[] memory targets = new address[](count); @@ -784,7 +788,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = _setupUserOpExecuteBatch( - account, + accountAddress, sessionKeyPrivKey, //Sign the userOp using the sessionKey's private key bytes(""), targets, @@ -792,13 +796,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { callData ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -817,24 +821,26 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { uint256 accountAdmin2PKey; (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); - assertEq(ManagedOpenfortAccount(payable(account)).owner(), accountAdmin); + assertEq(ManagedOpenfortAccount(payable(accountAddress)).owner(), accountAdmin); vm.expectRevert("Ownable: caller is not the owner"); - ManagedOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); + ManagedOpenfortAccount(payable(accountAddress)).transferOwnership(accountAdmin2); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); + ManagedOpenfortAccount(payable(accountAddress)).transferOwnership(accountAdmin2); vm.prank(accountAdmin2); - ManagedOpenfortAccount(payable(account)).acceptOwnership(); + ManagedOpenfortAccount(payable(accountAddress)).acceptOwnership(); // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the managed account wallet (deployer) call "count" vm.prank(accountAdmin2); - ManagedOpenfortAccount(payable(account)).execute(address(testCounter), 0, abi.encodeWithSignature("count()")); + ManagedOpenfortAccount(payable(accountAddress)).execute( + address(testCounter), 0, abi.encodeWithSignature("count()") + ); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -846,24 +852,24 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); + ManagedOpenfortAccount(payable(accountAddress)).transferOwnership(accountAdmin2); vm.prank(accountAdmin2); - ManagedOpenfortAccount(payable(account)).acceptOwnership(); + ManagedOpenfortAccount(payable(accountAddress)).acceptOwnership(); // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -878,7 +884,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(mockERC20.totalSupply(), 1); UserOperation[] memory userOp = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(mockERC20), @@ -886,7 +892,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -899,12 +905,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Test receive native tokens. */ function testReceiveNativeToken() public { - assertEq(address(account).balance, 0); + assertEq(address(accountAddress).balance, 0); vm.prank(accountAdmin); - (bool success,) = payable(account).call{value: 1000}(""); + (bool success,) = payable(accountAddress).call{value: 1000}(""); assert(success); - assertEq(address(account).balance, 1000); + assertEq(address(accountAddress).balance, 1000); } /* @@ -913,15 +919,15 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { function testTransferOutNativeToken() public { uint256 value = 1000; - assertEq(address(account).balance, 0); + assertEq(address(accountAddress).balance, 0); vm.prank(accountAdmin); - (bool success,) = payable(account).call{value: value}(""); - assertEq(address(account).balance, value); + (bool success,) = payable(accountAddress).call{value: value}(""); + assertEq(address(accountAddress).balance, value); assert(success); assertEq(beneficiary.balance, 0); UserOperation[] memory userOp = - _setupUserOpExecute(account, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); + _setupUserOpExecute(accountAddress, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); EntryPoint(entryPoint).handleOps(userOp, beneficiary); assertEq(beneficiary.balance, value); @@ -932,13 +938,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ // function testSimulateValidation() public { // // Verify that the counter is still set to 0 - // assertEq(testCounter.counters(account), 0); + // assertEq(testCounter.counters(accountAddress), 0); // UserOperation[] memory userOp = _setupUserOpExecute( // account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") // ); - // entryPoint.depositTo{value: 1000000000000000000}(account); + // entryPoint.depositTo{value: 1000000000000000000}(accountAddress); // // Expect the simulateValidation() to always revert // vm.expectRevert(); @@ -962,7 +968,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // entryPoint.simulateHandleOp(userOp[0], address(0), ""); // // Verify that the counter has not increased - // assertEq(testCounter.counters(account), 0); + // assertEq(testCounter.counters(accountAddress), 0); // } /* @@ -976,12 +982,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(address(managedOpenfortAccountImpl.entryPoint()), address(entryPoint)); // Try to use the old and new implementation before upgrade (should always behave with current values) - assertEq(MockV2ManagedOpenfortAccount(payable(account)).getLock(), 0); + assertEq(MockV2ManagedOpenfortAccount(payable(accountAddress)).getLock(), 0); vm.expectRevert(MustBeGuardian.selector); - MockV2ManagedOpenfortAccount(payable(account)).startRecovery(address(0)); + MockV2ManagedOpenfortAccount(payable(accountAddress)).startRecovery(address(0)); - assertEq(ManagedOpenfortAccount(payable(account)).getDeposit(), 0); - assertEq(MockV2ManagedOpenfortAccount(payable(account)).getDeposit(), 0); + assertEq(ManagedOpenfortAccount(payable(accountAddress)).getDeposit(), 0); + assertEq(MockV2ManagedOpenfortAccount(payable(accountAddress)).getDeposit(), 0); // Deploy the new account implementation MockV2ManagedOpenfortAccount mockV2ManagedOpenfortAccount = @@ -997,22 +1003,22 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Try to use the old and new implementation before upgrade (should always behave with current values) vm.expectRevert("disabled!"); - MockV2ManagedOpenfortAccount(payable(account)).getLock(); + MockV2ManagedOpenfortAccount(payable(accountAddress)).getLock(); vm.expectRevert("disabled!"); - ManagedOpenfortAccount(payable(account)).getLock(); + ManagedOpenfortAccount(payable(accountAddress)).getLock(); vm.expectRevert("disabled!"); - MockV2ManagedOpenfortAccount(payable(account)).startRecovery(address(0)); + MockV2ManagedOpenfortAccount(payable(accountAddress)).startRecovery(address(0)); vm.expectRevert("disabled!"); - ManagedOpenfortAccount(payable(account)).startRecovery(address(0)); + ManagedOpenfortAccount(payable(accountAddress)).startRecovery(address(0)); vm.expectRevert(); - ManagedOpenfortAccount(payable(account)).getDeposit(); + ManagedOpenfortAccount(payable(accountAddress)).getDeposit(); vm.expectRevert(); - MockV2ManagedOpenfortAccount(payable(account)).getDeposit(); + MockV2ManagedOpenfortAccount(payable(accountAddress)).getDeposit(); // Check that the EntryPoint is now upgraded too - assertEq(address(MockV2ManagedOpenfortAccount(payable(address(account))).entryPoint()), newEntryPoint); + assertEq(address(MockV2ManagedOpenfortAccount(payable(address(accountAddress))).entryPoint()), newEntryPoint); } function testFailIsValidSignature() public { @@ -1025,7 +1031,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { signer = ECDSA.recover(hash, signature); assertEq(accountAdmin, signer); // [PASS] - bytes4 valid = ManagedOpenfortAccount(payable(account)).isValidSignature(hash, signature); + bytes4 valid = ManagedOpenfortAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore } @@ -1041,7 +1047,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { signer = ECDSA.recover(hashMessage, signature); assertEq(accountAdmin, signer); // [PASS] - bytes4 valid = ManagedOpenfortAccount(payable(account)).isValidSignature(hash, signature); + bytes4 valid = ManagedOpenfortAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore } @@ -1072,19 +1078,19 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, hash)); (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = - IERC5267(account).eip712Domain(); + IERC5267(accountAddress).eip712Domain(); bytes32 domainSeparator = keccak256( abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) ); - bytes memory signature = getEIP712SignatureFrom(account, structHash, accountAdminPKey); + bytes memory signature = getEIP712SignatureFrom(accountAddress, structHash, accountAdminPKey); bytes32 hash712 = domainSeparator.toTypedDataHash(structHash); address signer = hash712.recover(signature); assertEq(accountAdmin, signer); // [PASS] - bytes4 valid = ManagedOpenfortAccount(payable(account)).isValidSignature(hash, signature); + bytes4 valid = ManagedOpenfortAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, MAGICVALUE); // SHOULD PASS } @@ -1096,7 +1102,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Test locking the Openfort account using the default guardian. */ function testLockAccount() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); assertEq(openfortAccount.isLocked(), false); assertEq(openfortAccount.getLock(), 0); @@ -1124,7 +1130,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Test unlocking the Openfort account using the default guardian. */ function testUnlockAccount() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); assertEq(openfortAccount.isLocked(), false); assertEq(openfortAccount.getLock(), 0); @@ -1164,7 +1170,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Successfully propose a guardian and confirm it after SECURITY_PERIOD */ function testAddEOAGuardian() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); openfortAccount.getGuardians(); @@ -1213,7 +1219,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. */ function testAddEOAGuardianExpired() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1258,7 +1264,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. */ function testAddEOAGuardianExpiredThenReAdd() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1326,7 +1332,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. */ function testAddEOAGuardianDuplicatedPorposal() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1368,7 +1374,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Only the owner can cancel an ongoing proposal. */ function testAddEOAGuardianCancel() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1424,7 +1430,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Successfully propose a guardian and confirm it after SECURITY_PERIOD */ function testAddOwnerAsGuardianNotAllowed() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); openfortAccount.getGuardians(); @@ -1459,7 +1465,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Successfully propose guardians and confirm them after SECURITY_PERIOD */ function testAddMultipleEOAGuardians() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); openfortAccount.getGuardians(); @@ -1510,7 +1516,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Only the owner can revoke a guardian. */ function testRevokeGuardian() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1569,7 +1575,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Only the owner can revoke a guardian. */ function testRevokeDefaultGuardian() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); openfortAccount.getGuardians(); @@ -1625,7 +1631,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Only the owner can revoke a guardian. */ function testRevokeAllGuardians() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1704,7 +1710,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD + SECURITY_WINDOW. */ function testRevokeEOAGuardianExpired() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1745,7 +1751,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD and SECURITY_WINDOW. */ function testRevokeEOAGuardianDuplicatedPorposal() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1793,7 +1799,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Test revoking the default guardian and add it back. */ function testRevokeDefaultGuardianAndAddBack() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1833,7 +1839,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Only the owner can revoke a guardian and cancel its revocation before confirming. */ function testCancelRevokeGuardian() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1900,7 +1906,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Random extra tests to mess up with the logic */ function testMessingUpWithGuardianRegister() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Create 4 friends address friendAccount; @@ -1949,7 +1955,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Check the correct functionality of startRecovery() */ function testStartRecovery() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); vm.expectRevert(MustBeGuardian.selector); openfortAccount.startRecovery(OPENFORT_GUARDIAN); @@ -1968,7 +1974,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Checks that incorrect parameters should always fail when trying to complete a recovery */ function testBasicChecksCompleteRecovery() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); vm.prank(OPENFORT_GUARDIAN); openfortAccount.startRecovery(address(beneficiary)); @@ -1997,7 +2003,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Ownership is transferred to beneficiary */ function testBasicCompleteRecovery() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK vm.prank(OPENFORT_GUARDIAN); @@ -2009,7 +2015,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, OPENFORT_GUARDIAN_PKEY); skip(RECOVERY_PERIOD + 1); openfortAccount.completeRecovery(signatures); @@ -2025,7 +2031,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * @notice Remember that signatures need to be ordered by the guardian's address. */ function test3GuardiansCompleteRecovery() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Create two friends address friendAccount; @@ -2065,8 +2071,8 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(accountAddress, structHash, friendAccountPK); skip(RECOVERY_PERIOD + 1); openfortAccount.completeRecovery(signatures); @@ -2082,7 +2088,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * @notice Remember that signatures need to be ordered by the guardian's address. */ function test3GuardiansUnorderedCompleteRecovery() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Create two friends address friendAccount; @@ -2122,8 +2128,8 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccountPK); // Unsorted! - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccountPK); // Unsorted! + signatures[1] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); skip(RECOVERY_PERIOD + 1); vm.expectRevert(InvalidRecoverySignatures.selector); @@ -2141,7 +2147,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * @notice Remember that signatures need to be ordered by the guardian's address. */ function test4GuardiansNoDefaultCompleteRecovery() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Create 4 friends address friendAccount; @@ -2196,8 +2202,8 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(accountAddress, structHash, friendAccountPK); skip(RECOVERY_PERIOD + 1); openfortAccount.completeRecovery(signatures); @@ -2213,7 +2219,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * @notice Remember that signatures need to be ordered by the guardian's address. */ function test3GuardiansFailCompleteRecovery() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Create two friends address friendAccount; @@ -2253,8 +2259,8 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { keccak256(abi.encode(RECOVER_TYPEHASH, factoryAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(accountAddress, structHash, friendAccountPK); skip(RECOVERY_PERIOD + 1); vm.expectRevert(InvalidRecoverySignatures.selector); @@ -2269,7 +2275,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Testing the functionality to cancel a recovery process */ function testCancelRecovery() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK vm.prank(OPENFORT_GUARDIAN); @@ -2287,7 +2293,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, OPENFORT_GUARDIAN_PKEY); skip(RECOVERY_PERIOD + 1); vm.expectRevert(NoOngoingRecovery.selector); @@ -2301,7 +2307,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Testing the startRecovery twice in a row */ function testStartRecoveryTwice() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK vm.prank(OPENFORT_GUARDIAN); @@ -2327,7 +2333,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should not be allowed. */ function testTransferOwnerNotGuardian() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Create a friend EOA address friendAccount = makeAddr("friend"); @@ -2352,7 +2358,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Should be allowed. */ function testTransferOwner() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Create a new owner EOA address newOwner = makeAddr("newOwner"); @@ -2375,7 +2381,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * that isGuardianOrGuardianSigner() always returns false. */ function testStubFakeMockTempisGuardian() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(account)); + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); assertEq(openfortAccount.isGuardianOrGuardianSigner(OPENFORT_GUARDIAN), false); } } diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 61f14fe..233d25e 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -7,7 +7,7 @@ import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC20} from "contracts/mock/MockERC20.sol"; -import {IBaseOpenfortAccount} from "contracts/interfaces/IBaseOpenfortAccount.sol"; +import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {UpgradeableOpenfortProxy} from "contracts/core/upgradeable/UpgradeableOpenfortProxy.sol"; @@ -67,7 +67,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); // Create an upgradeable account wallet and get its address - account = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); + accountAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); // deploy a new TestCounter testCounter = new TestCounter(); @@ -180,16 +180,16 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterDirect() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).execute( + IBaseRecoverableAccount(payable(accountAddress)).execute( address(testCounter), 0, abi.encodeWithSignature("count()") ); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -198,19 +198,19 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaEntrypoint() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -219,7 +219,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaEntrypointBatching() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); uint256 count = 3; address[] memory targets = new address[](count); @@ -233,15 +233,15 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, accountAdminPKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 3); + assertEq(testCounter.counters(accountAddress), 3); } /* @@ -249,23 +249,23 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyNotregistered() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -273,7 +273,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaSessionKey() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -281,19 +281,21 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey( + sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist + ); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -302,7 +304,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testRegisterSessionKeyViaEntrypoint() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -310,7 +312,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOp( - account, + accountAddress, accountAdminPKey, bytes(""), abi.encodeWithSignature( @@ -323,25 +325,25 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -351,7 +353,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailRegisterSessionKeyViaEntrypoint2ndKey() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -359,7 +361,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; UserOperation[] memory userOp = _setupUserOp( - account, + accountAddress, accountAdminPKey, bytes(""), abi.encodeWithSignature( @@ -372,32 +374,32 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); address sessionKeyAttack; uint256 sessionKeyPrivKeyAttack; (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); userOp = _setupUserOp( - account, + accountAddress, sessionKeyPrivKey, bytes(""), abi.encodeWithSignature( @@ -410,7 +412,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -422,45 +424,45 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailAttackRegisterSessionKeyViaEntrypoint2ndKey() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); UserOperation[] memory userOp = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), - account, + accountAddress, 0, abi.encodeWithSignature("registerSessionKey(address,uint48,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1, 10) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); // Verify that the registered key is not a MasterKey nor has whitelisting bool isMasterKey; bool isWhitelisted; - (,,, isMasterKey, isWhitelisted,) = UpgradeableOpenfortAccount(payable(account)).sessionKeys(sessionKey); + (,,, isMasterKey, isWhitelisted,) = IBaseRecoverableAccount(payable(accountAddress)).sessionKeys(sessionKey); assert(!isMasterKey); assert(!isWhitelisted); @@ -469,15 +471,15 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { (sessionKeyAttack, sessionKeyPrivKeyAttack) = makeAddrAndKey("sessionKeyAttack"); userOp = _setupUserOpExecute( - account, + accountAddress, sessionKeyPrivKey, bytes(""), - account, + accountAddress, 0, abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKeyAttack, 0, 2 ** 48 - 1) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -488,7 +490,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaSessionKeyExpired() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -497,20 +499,20 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.warp(100); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); vm.expectRevert(); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -518,7 +520,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyRevoked() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -526,20 +528,20 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); - UpgradeableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); + IBaseRecoverableAccount(payable(accountAddress)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -547,7 +549,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyReachLimit() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -557,28 +559,28 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has only increased by one - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -586,7 +588,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyReachLimitBatching() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -596,7 +598,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // We are now in block 100, but our session key is valid until block 150 vm.warp(100); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); uint256 count = 3; address[] memory targets = new address[](count); @@ -610,15 +612,15 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -626,7 +628,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailRevokeSessionKeyInvalidUser() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -634,21 +636,21 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); vm.prank(beneficiary); - UpgradeableOpenfortAccount(payable(account)).revokeSessionKey(sessionKey); + IBaseRecoverableAccount(payable(accountAddress)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -656,7 +658,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaSessionKeyWhitelisting() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -665,19 +667,19 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -685,7 +687,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyWhitelistingTooBig() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -693,19 +695,19 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](11); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -713,7 +715,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testIncrementCounterViaSessionKeyWhitelistingBatch() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; @@ -722,12 +724,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); // Verify that the registered key is not a MasterKey but has whitelisting bool isMasterKey; bool isWhitelisted; - (,,, isMasterKey, isWhitelisted,) = UpgradeableOpenfortAccount(payable(account)).sessionKeys(sessionKey); + (,,, isMasterKey, isWhitelisted,) = IBaseRecoverableAccount(payable(accountAddress)).sessionKeys(sessionKey); assert(!isMasterKey); assert(isWhitelisted); @@ -743,15 +745,15 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(account, sessionKeyPrivKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 3); + assertEq(testCounter.counters(accountAddress), 3); } /* @@ -759,28 +761,28 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyWhitelistingWrongAddress() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory whitelist = new address[](1); - whitelist[0] = address(account); + whitelist[0] = address(accountAddress); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( - account, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -788,16 +790,16 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFailIncrementCounterViaSessionKeyWhitelistingBatchWrongAddress() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); address sessionKey; uint256 sessionKeyPrivKey; (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory whitelist = new address[](1); - whitelist[0] = address(account); + whitelist[0] = address(accountAddress); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); uint256 count = 3; address[] memory targets = new address[](count); @@ -811,7 +813,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = _setupUserOpExecuteBatch( - account, + accountAddress, sessionKeyPrivKey, //Sign the userOp using the sessionKey's private key bytes(""), targets, @@ -819,13 +821,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { callData ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -845,24 +847,24 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); vm.expectRevert("Ownable: caller is not the owner"); - UpgradeableOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); + IBaseRecoverableAccount(payable(accountAddress)).transferOwnership(accountAdmin2); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); + IBaseRecoverableAccount(payable(accountAddress)).transferOwnership(accountAdmin2); vm.prank(accountAdmin2); - UpgradeableOpenfortAccount(payable(account)).acceptOwnership(); + IBaseRecoverableAccount(payable(accountAddress)).acceptOwnership(); // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" vm.prank(accountAdmin2); - UpgradeableOpenfortAccount(payable(account)).execute( + IBaseRecoverableAccount(payable(accountAddress)).execute( address(testCounter), 0, abi.encodeWithSignature("count()") ); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -874,24 +876,24 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).transferOwnership(accountAdmin2); + IBaseRecoverableAccount(payable(accountAddress)).transferOwnership(accountAdmin2); vm.prank(accountAdmin2); - UpgradeableOpenfortAccount(payable(account)).acceptOwnership(); + IBaseRecoverableAccount(payable(accountAddress)).acceptOwnership(); // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -906,7 +908,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(mockERC20.totalSupply(), 1); UserOperation[] memory userOp = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(mockERC20), @@ -914,7 +916,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -927,12 +929,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Test receive native tokens. */ function testReceiveNativeToken() public { - assertEq(address(account).balance, 0); + assertEq(address(accountAddress).balance, 0); vm.prank(accountAdmin); - (bool success,) = payable(account).call{value: 1000}(""); + (bool success,) = payable(accountAddress).call{value: 1000}(""); assert(success); - assertEq(address(account).balance, 1000); + assertEq(address(accountAddress).balance, 1000); } /* @@ -941,15 +943,15 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testTransferOutNativeToken() public { uint256 value = 1000; - assertEq(address(account).balance, 0); + assertEq(address(accountAddress).balance, 0); vm.prank(accountAdmin); - (bool success,) = payable(account).call{value: value}(""); - assertEq(address(account).balance, value); + (bool success,) = payable(accountAddress).call{value: value}(""); + assertEq(address(accountAddress).balance, value); assert(success); assertEq(beneficiary.balance, 0); UserOperation[] memory userOp = - _setupUserOpExecute(account, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); + _setupUserOpExecute(accountAddress, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); EntryPoint(entryPoint).handleOps(userOp, beneficiary); assertEq(beneficiary.balance, value); @@ -960,52 +962,52 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testSimulateValidation() public { // Verify that the counter is still set to 0 - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(account); + entryPoint.depositTo{value: 1000000000000000000}(accountAddress); // Expect the simulateValidation() to always revert vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* * Create an account and upgrade its implementation */ function testUpgradeAccount() public { - assertEq(UpgradeableOpenfortAccount(payable(account)).owner(), address(accountAdmin)); - assertEq(address(IBaseOpenfortAccount(payable(account)).entryPoint()), address(entryPoint)); + assertEq(IBaseRecoverableAccount(payable(accountAddress)).owner(), address(accountAdmin)); + assertEq(address(IBaseRecoverableAccount(payable(accountAddress)).entryPoint()), address(entryPoint)); MockV2UpgradeableOpenfortAccount newAccountImplementation = new MockV2UpgradeableOpenfortAccount(); - UpgradeableOpenfortProxy p = UpgradeableOpenfortProxy(payable(account)); + UpgradeableOpenfortProxy p = UpgradeableOpenfortProxy(payable(accountAddress)); // Printing account address and the implementation address - console.log("Account address (proxy): ", account); + console.log("Account address (proxy): ", accountAddress); console.log("Implementation address (old): ", p.implementation()); vm.expectRevert("Ownable: caller is not the owner"); - UpgradeableOpenfortAccount(payable(account)).upgradeTo(address(newAccountImplementation)); + UpgradeableOpenfortAccount(payable(accountAddress)).upgradeTo(address(newAccountImplementation)); vm.expectRevert(); - MockV2UpgradeableOpenfortAccount(payable(account)).easterEgg(); + MockV2UpgradeableOpenfortAccount(payable(accountAddress)).easterEgg(); vm.prank(accountAdmin); - UpgradeableOpenfortAccount(payable(account)).upgradeTo(address(newAccountImplementation)); + UpgradeableOpenfortAccount(payable(accountAddress)).upgradeTo(address(newAccountImplementation)); // Notice that, even though we bind the address to the old implementation, entryPoint() is now changed assertEq( - address(IBaseOpenfortAccount(payable(account)).entryPoint()), + address(IBaseRecoverableAccount(payable(accountAddress)).entryPoint()), address(0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF) ); - assertEq(MockV2UpgradeableOpenfortAccount(payable(account)).easterEgg(), 42); + assertEq(MockV2UpgradeableOpenfortAccount(payable(accountAddress)).easterEgg(), 42); // Printing account address and the implementation address. Impl address should have changed - console.log("Account address (proxy): ", account); + console.log("Account address (proxy): ", accountAddress); console.log("Implementation address (new): ", p.implementation()); } @@ -1051,7 +1053,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { signer = ECDSA.recover(hash, signature); assertEq(accountAdmin, signer); // [PASS] - bytes4 valid = UpgradeableOpenfortAccount(payable(account)).isValidSignature(hash, signature); + bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore } @@ -1067,7 +1069,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { signer = ECDSA.recover(hashMessage, signature); assertEq(accountAdmin, signer); // [PASS] - bytes4 valid = UpgradeableOpenfortAccount(payable(account)).isValidSignature(hash, signature); + bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore } @@ -1098,19 +1100,19 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, hash)); (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = - IERC5267(account).eip712Domain(); + IERC5267(accountAddress).eip712Domain(); bytes32 domainSeparator = keccak256( abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) ); - bytes memory signature = getEIP712SignatureFrom(account, structHash, accountAdminPKey); + bytes memory signature = getEIP712SignatureFrom(accountAddress, structHash, accountAdminPKey); bytes32 hash712 = domainSeparator.toTypedDataHash(structHash); address signer = hash712.recover(signature); assertEq(accountAdmin, signer); // [PASS] - bytes4 valid = UpgradeableOpenfortAccount(payable(account)).isValidSignature(hash, signature); + bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, MAGICVALUE); // SHOULD PASS } @@ -1122,7 +1124,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Test locking the Openfort account using the default guardian. */ function testLockAccount() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); assertEq(openfortAccount.isLocked(), false); assertEq(openfortAccount.getLock(), 0); @@ -1150,7 +1152,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Test unlocking the Openfort account using the default guardian. */ function testUnlockAccount() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); assertEq(openfortAccount.isLocked(), false); assertEq(openfortAccount.getLock(), 0); @@ -1190,7 +1192,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Successfully propose a guardian and confirm it after SECURITY_PERIOD */ function testAddEOAGuardian() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); openfortAccount.getGuardians(); @@ -1239,7 +1241,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. */ function testAddEOAGuardianExpired() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1284,7 +1286,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. */ function testAddEOAGuardianExpiredThenReAdd() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1352,7 +1354,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * An expired proposal cannot be accepted. A proposal expires after SECURITY_PERIOD and SECURITY_WINDOW. */ function testAddEOAGuardianDuplicatedPorposal() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1394,7 +1396,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Only the owner can cancel an ongoing proposal. */ function testAddEOAGuardianCancel() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1450,7 +1452,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Successfully propose a guardian and confirm it after SECURITY_PERIOD */ function testAddOwnerAsGuardianNotAllowed() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); openfortAccount.getGuardians(); @@ -1485,7 +1487,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Successfully propose guardians and confirm them after SECURITY_PERIOD */ function testAddMultipleEOAGuardians() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); openfortAccount.getGuardians(); @@ -1536,7 +1538,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Only the owner can revoke a guardian. */ function testRevokeGuardian() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1595,7 +1597,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Only the owner can revoke a guardian. */ function testRevokeDefaultGuardian() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); openfortAccount.getGuardians(); @@ -1651,7 +1653,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Only the owner can revoke a guardian. */ function testRevokeAllGuardians() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1730,7 +1732,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD + SECURITY_WINDOW. */ function testRevokeEOAGuardianExpired() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1771,7 +1773,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * An expired revocation cannot be confirmed. A revocation expires after SECURITY_PERIOD and SECURITY_WINDOW. */ function testRevokeEOAGuardianDuplicatedPorposal() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1819,7 +1821,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Test revoking the default guardian and add it back. */ function testRevokeDefaultGuardianAndAddBack() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1859,7 +1861,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Only the owner can revoke a guardian and cancel its revocation before confirming. */ function testCancelRevokeGuardian() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Verify that the number of guardians is 1 (default) assertEq(openfortAccount.guardianCount(), 1); @@ -1926,7 +1928,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Random extra tests to mess up with the logic */ function testMessingUpWithGuardianRegister() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Create 4 friends address friendAccount; @@ -1976,7 +1978,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Check the correct functionality of startRecovery() */ function testStartRecovery() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); vm.expectRevert(MustBeGuardian.selector); openfortAccount.startRecovery(OPENFORT_GUARDIAN); @@ -1995,7 +1997,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Checks that incorrect parameters should always fail when trying to complete a recovery */ function testBasicChecksCompleteRecovery() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); vm.prank(OPENFORT_GUARDIAN); openfortAccount.startRecovery(address(beneficiary)); @@ -2024,7 +2026,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Ownership is transferred to beneficiary */ function testBasicCompleteRecovery() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK vm.prank(OPENFORT_GUARDIAN); @@ -2036,7 +2038,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, OPENFORT_GUARDIAN_PKEY); skip(RECOVERY_PERIOD + 1); openfortAccount.completeRecovery(signatures); @@ -2052,7 +2054,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * @notice Remember that signatures need to be ordered by the guardian's address. */ function test3GuardiansCompleteRecovery() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Create two friends address friendAccount; @@ -2093,8 +2095,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { bytes[] memory signatures = new bytes[](2); // Using friendAccount2 first because it has a lower address - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); + signatures[1] = getEIP712SignatureFrom(accountAddress, structHash, friendAccountPK); skip(RECOVERY_PERIOD + 1); openfortAccount.completeRecovery(signatures); @@ -2110,7 +2112,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * @notice Remember that signatures need to be ordered by the guardian's address. */ function test3GuardiansUnorderedCompleteRecovery() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Create two friends address friendAccount; @@ -2150,8 +2152,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccountPK); // Unsorted! - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccountPK); // Unsorted! + signatures[1] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); skip(RECOVERY_PERIOD + 1); vm.expectRevert(InvalidRecoverySignatures.selector); @@ -2169,7 +2171,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * @notice Remember that signatures need to be ordered by the guardian's address. */ function test4GuardiansNoDefaultCompleteRecovery() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Create 4 friends address friendAccount; @@ -2224,8 +2226,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(accountAddress, structHash, friendAccountPK); skip(RECOVERY_PERIOD + 1); openfortAccount.completeRecovery(signatures); @@ -2241,7 +2243,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * @notice Remember that signatures need to be ordered by the guardian's address. */ function test3GuardiansFailCompleteRecovery() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Create two friends address friendAccount; @@ -2281,8 +2283,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { keccak256(abi.encode(RECOVER_TYPEHASH, factoryAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); bytes[] memory signatures = new bytes[](2); - signatures[0] = getEIP712SignatureFrom(account, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address - signatures[1] = getEIP712SignatureFrom(account, structHash, friendAccountPK); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address + signatures[1] = getEIP712SignatureFrom(accountAddress, structHash, friendAccountPK); skip(RECOVERY_PERIOD + 1); vm.expectRevert(InvalidRecoverySignatures.selector); @@ -2297,7 +2299,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Testing the functionality to cancel a recovery process */ function testCancelRecovery() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK vm.prank(OPENFORT_GUARDIAN); @@ -2315,7 +2317,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(account, structHash, OPENFORT_GUARDIAN_PKEY); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, OPENFORT_GUARDIAN_PKEY); skip(RECOVERY_PERIOD + 1); vm.expectRevert(NoOngoingRecovery.selector); @@ -2329,7 +2331,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Testing the startRecovery twice in a row */ function testStartRecoveryTwice() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK vm.prank(OPENFORT_GUARDIAN); @@ -2355,7 +2357,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should not be allowed. */ function testTransferOwnerNotGuardian() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Create a friend EOA address friendAccount = makeAddr("friend"); @@ -2380,7 +2382,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Should be allowed. */ function testTransferOwner() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Create a new owner EOA address newOwner = makeAddr("newOwner"); @@ -2403,7 +2405,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * that isGuardianOrGuardianSigner() always returns false. */ function testStubFakeMockTempisGuardian() public { - UpgradeableOpenfortAccount openfortAccount = UpgradeableOpenfortAccount(payable(account)); + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); assertEq(openfortAccount.isGuardianOrGuardianSigner(OPENFORT_GUARDIAN), false); } } diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index 5e30c03..992de07 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -211,9 +211,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { mockERC20 = new MockERC20(); mockERC20.mint(address(this), 1_000 * 10 ** 18); - // Create an static account wallet and get its address + // Create an Openfort account and get its address vm.prank(factoryAdmin); - account = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); + accountAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); } /* @@ -447,7 +447,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, "0x1234"); UserOperation[] memory userOp = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(testCounter), @@ -461,7 +461,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { entryPoint.simulateValidation(userOp[0]); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -473,7 +473,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); // MOCKSIG, "1", MOCKSIG to make sure we send 65 bytes as sig UserOperation[] memory userOp = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(testCounter), @@ -487,7 +487,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { entryPoint.simulateValidation(userOp[0]); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -500,7 +500,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); UserOperation[] memory userOps = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(testCounter), @@ -555,7 +555,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); //Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -568,7 +568,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); UserOperation[] memory userOps = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(testCounter), @@ -623,7 +623,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); //Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); } /* @@ -631,9 +631,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * Using ERC20. Should work */ function testPaymasterUserOpERC20ValidSigDiffMaxPriorityFeePerGas() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); @@ -641,7 +641,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(mockERC20), @@ -697,7 +697,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has decreased - assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + assert(mockERC20.balanceOf(accountAddress) < TESTTOKEN_ACCOUNT_PREFUND); } /* @@ -705,9 +705,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * Using ERC20. Should work */ function testPaymasterUserOpERC20ValidSig() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); @@ -715,7 +715,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(mockERC20), @@ -770,7 +770,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has decreased - assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + assert(mockERC20.balanceOf(accountAddress) < TESTTOKEN_ACCOUNT_PREFUND); } /* @@ -778,9 +778,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * Using FIXED ERC20. Should work */ function testPaymasterUserOpERC20FixedValidSig() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); uint256 pricePerTransaction = 10 ** 18; bytes memory dataEncoded = mockPaymasterDataERC20Fixed(paymasterAdmin, pricePerTransaction); @@ -789,7 +789,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(mockERC20), @@ -844,7 +844,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has decreased - assert(mockERC20.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); + assert(mockERC20.balanceOf(accountAddress) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); } /* @@ -852,11 +852,11 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * ExecBatch. Using dynamic ERC20. Should work */ function testPaymasterUserOpERC20ValidSigExecBatch() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); @@ -876,8 +876,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { callData[1] = abi.encodeWithSignature("count()"); // Create a userOp to let the paymaster use our mockERC20s - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + UserOperation[] memory userOps = _setupUserOpExecuteBatch( + accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + ); OpenfortPaymasterV2.PolicyStrategy memory strategy; strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; @@ -925,8 +926,8 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has decreased - assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testCounter.counters(account), 1); + assert(mockERC20.balanceOf(accountAddress) < TESTTOKEN_ACCOUNT_PREFUND); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -934,11 +935,11 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * ExecBatch. Using fixed ERC20. Should work */ function testPaymasterUserOpERC20FixedValidSigExecBatch() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); uint256 pricePerTransaction = 10 ** 18; @@ -960,8 +961,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { callData[1] = abi.encodeWithSignature("count()"); // Create a userOp to let the paymaster use our mockERC20s - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + UserOperation[] memory userOps = _setupUserOpExecuteBatch( + accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + ); OpenfortPaymasterV2.PolicyStrategy memory strategy; strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; @@ -1009,8 +1011,8 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has decreased - assert(mockERC20.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); - assertEq(testCounter.counters(account), 1); + assert(mockERC20.balanceOf(accountAddress) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -1018,11 +1020,11 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * ExecBatch. Using fixed ERC20 expensive. Should work */ function testPaymasterUserOpERC20FixedExpensiveValidSigExecBatch() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); uint256 pricePerTransaction = 10; @@ -1044,8 +1046,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { callData[1] = abi.encodeWithSignature("count()"); // Create a userOp to let the paymaster use our mockERC20s - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + UserOperation[] memory userOps = _setupUserOpExecuteBatch( + accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + ); OpenfortPaymasterV2.PolicyStrategy memory strategy; strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; @@ -1093,8 +1096,8 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has decreased - assert(mockERC20.balanceOf(account) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); - assertEq(testCounter.counters(account), 1); + assert(mockERC20.balanceOf(accountAddress) == TESTTOKEN_ACCOUNT_PREFUND - pricePerTransaction); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -1120,8 +1123,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { callData[1] = abi.encodeWithSignature("count()"); // Create a userOp to let the paymaster use our mockERC20s - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + UserOperation[] memory userOps = _setupUserOpExecuteBatch( + accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + ); OpenfortPaymasterV2.PolicyStrategy memory strategy; strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; @@ -1170,7 +1174,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); //Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); } /* @@ -1179,11 +1183,11 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * Test showing that failing to repay in ERC20 still spends some of Paymaster's deposit (DoS) */ function testFailPaymasterUserOpERC20ValidSigExecBatchInsufficientERC20() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, 100); - assertEq(mockERC20.balanceOf(account), 100); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, 100); + assertEq(mockERC20.balanceOf(accountAddress), 100); - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); @@ -1203,8 +1207,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { callData[1] = abi.encodeWithSignature("count()"); // Create a userOp to let the paymaster use our mockERC20s - UserOperation[] memory userOps = - _setupUserOpExecuteBatch(account, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData); + UserOperation[] memory userOps = _setupUserOpExecuteBatch( + accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + ); OpenfortPaymasterV2.PolicyStrategy memory strategy; strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; @@ -1252,9 +1257,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has the same deposit assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has not decreased - assertEq(mockERC20.balanceOf(account), 100); + assertEq(mockERC20.balanceOf(accountAddress), 100); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); // If this fails, it would mean: // 1- That the paymaster has spent some of its deposit @@ -1267,9 +1272,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * Using ERC20. Should work. */ function testFailPaymasterUserOpERC20ValidSigSmallApprove() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(paymasterAdmin); @@ -1277,7 +1282,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(mockERC20), @@ -1332,9 +1337,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has the same deposit assert(paymasterDepositBefore == openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has not decreased - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); // Verify that the counter has not increased - assertEq(testCounter.counters(account), 0); + assertEq(testCounter.counters(accountAddress), 0); // If this fails, it would mean: // 1- That the paymaster has spent some of its deposit @@ -1347,9 +1352,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * Using ERC20 and a 3rd party depositor. Should work */ function testPaymasterUserOpERC20ValidSigDepositor() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); vm.prank(factoryAdmin); openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); @@ -1363,7 +1368,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(mockERC20), @@ -1418,7 +1423,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has decreased - assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + assert(mockERC20.balanceOf(accountAddress) < TESTTOKEN_ACCOUNT_PREFUND); assert(openfortPaymaster.getDeposit() < 100 ether); assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); @@ -1431,9 +1436,9 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * Using ERC20 (fixed rate) and a 3rd party depositor. Should work */ function testPaymasterUserOpERC20FixedValidSigDepositor() public { - assertEq(mockERC20.balanceOf(account), 0); - mockERC20.mint(account, TESTTOKEN_ACCOUNT_PREFUND); - assertEq(mockERC20.balanceOf(account), TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), 0); + mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); + assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); vm.prank(factoryAdmin); openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); @@ -1449,7 +1454,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(mockERC20), @@ -1504,7 +1509,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); // Verify that the balance of the smart account has decreased - assert(mockERC20.balanceOf(account) < TESTTOKEN_ACCOUNT_PREFUND); + assert(mockERC20.balanceOf(accountAddress) < TESTTOKEN_ACCOUNT_PREFUND); assert(openfortPaymaster.getDeposit() < 100 ether); assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); @@ -1534,7 +1539,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { */ function test_requireFromEntryPoint() public { UserOperation[] memory userOpAux = _setupUserOpExecute( - account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()"), "" + accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()"), "" ); vm.prank(paymasterAdmin); @@ -1626,7 +1631,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { console.logBytes(paymasterAndData); UserOperation[] memory userOps = _setupUserOpExecute( - account, + accountAddress, accountAdminPKey, bytes(""), address(testCounter), @@ -1685,7 +1690,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Verify that the paymaster has less deposit now assert(paymasterDepositBefore > openfortPaymaster.getDeposit()); //Verify that the counter has increased - assertEq(testCounter.counters(account), 1); + assertEq(testCounter.counters(accountAddress), 1); assert(openfortPaymaster.getDeposit() < 53 ether); // less than 53 because the total cost have decreased assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // more than 50 because the dust has gone to the owner deposit From 88ed5f1a355e06b4644c54e54d96d2fab49e2764 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 17:08:58 +0100 Subject: [PATCH 37/83] Improving tests --- .../IUpgradeableOpenfortAccount.sol | 8 ++++ .../managed/ManagedOpenfortAccountTest.t.sol | 40 +++++++++---------- .../UpgradeableOpenfortAccountTest.t.sol | 35 ++++++++-------- 3 files changed, 46 insertions(+), 37 deletions(-) create mode 100644 contracts/interfaces/IUpgradeableOpenfortAccount.sol diff --git a/contracts/interfaces/IUpgradeableOpenfortAccount.sol b/contracts/interfaces/IUpgradeableOpenfortAccount.sol new file mode 100644 index 0000000..a2cfed5 --- /dev/null +++ b/contracts/interfaces/IUpgradeableOpenfortAccount.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {IBaseRecoverableAccount} from "./IBaseRecoverableAccount.sol"; + +interface IUpgradeableOpenfortAccount is IBaseRecoverableAccount { + function updateEntryPoint(address _newEntrypoint) external; +} diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 988a05a..f939529 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -17,12 +17,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; ManagedOpenfortAccount public managedOpenfortAccountImpl; - ManagedOpenfortFactory public managedOpenfortFactory; + ManagedOpenfortFactory public openfortFactory; /** * @notice Initialize the ManagedOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the managedOpenfortAccountImpl and managedOpenfortFactory/Beacon + * - factoryAdmin is the deployer (and owner) of the managedOpenfortAccountImpl and openfortFactory/Beacon * - accountAdmin is the account used to deploy new managed accounts using the factory * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps @@ -51,7 +51,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // deploy account implementation managedOpenfortAccountImpl = new ManagedOpenfortAccount{salt: versionSalt}(); // deploy account factory (beacon) - managedOpenfortFactory = new ManagedOpenfortFactory{salt: versionSalt}( + openfortFactory = new ManagedOpenfortFactory{salt: versionSalt}( factoryAdmin, address(entryPoint), address(managedOpenfortAccountImpl), @@ -62,7 +62,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { OPENFORT_GUARDIAN ); // Create an managed account wallet and get its address - accountAddress = managedOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); + accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1"); // deploy a new TestCounter testCounter = new TestCounter{salt: versionSalt}(); // deploy a new MockERC20 (ERC20) @@ -92,7 +92,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { function testCreateAccountWithNonceViaFactory() public { // Get the counterfactual address vm.prank(factoryAdmin); - address account2 = managedOpenfortFactory.getAddressWithNonce(accountAdmin, "2"); + address account2 = openfortFactory.getAddressWithNonce(accountAdmin, "2"); // Expect that we will see an event containing the account and admin vm.expectEmit(true, true, false, true); @@ -100,15 +100,15 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Deploy a managed account to the counterfactual address vm.prank(factoryAdmin); - managedOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); + openfortFactory.createAccountWithNonce(accountAdmin, "2"); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - managedOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); + openfortFactory.createAccountWithNonce(accountAdmin, "2"); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); - assertEq(account2, managedOpenfortFactory.getAddressWithNonce(accountAdmin, "2")); + assertEq(account2, openfortFactory.getAddressWithNonce(accountAdmin, "2")); } /* @@ -117,7 +117,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { function testFuzzCreateAccountWithNonceViaFactory(address _adminAddress, bytes32 _nonce) public { // Get the counterfactual address vm.prank(factoryAdmin); - address account2 = managedOpenfortFactory.getAddressWithNonce(_adminAddress, _nonce); + address account2 = openfortFactory.getAddressWithNonce(_adminAddress, _nonce); // Expect that we will see an event containing the account and admin vm.expectEmit(true, true, false, true); @@ -125,15 +125,15 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Deploy a managed account to the counterfactual address vm.prank(factoryAdmin); - managedOpenfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - managedOpenfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); - assertEq(account2, managedOpenfortFactory.getAddressWithNonce(_adminAddress, _nonce)); + assertEq(account2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); } /* @@ -146,12 +146,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // revert(); // Make sure the smart account does not have any code yet - address account2 = managedOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); + address account2 = openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); assertEq(account2.code.length, 0); bytes memory initCallData = abi.encodeWithSignature("createAccountWithNonce(address,bytes32)", accountAdmin, bytes32("2")); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(managedOpenfortFactory)), initCallData); + bytes memory initCode = abi.encodePacked(abi.encodePacked(address(openfortFactory)), initCallData); UserOperation[] memory userOpCreateAccount = _setupUserOpExecute(account2, accountAdminPKey, initCode, address(0), 0, bytes("")); @@ -168,7 +168,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assert(account2.code.length > 0); // Make sure the counterfactual address has not been altered - assertEq(account2, managedOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); + assertEq(account2, openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); } /* @@ -952,12 +952,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // // Test addStake. Make sure it checks for owner and alue passed. // vm.expectRevert("Ownable: caller is not the owner"); - // managedOpenfortFactory.addStake{value: 10000000000000000}(99); + // openfortFactory.addStake{value: 10000000000000000}(99); // vm.prank(factoryAdmin); // vm.expectRevert("no stake specified"); - // managedOpenfortFactory.addStake(99); + // openfortFactory.addStake(99); // vm.prank(factoryAdmin); - // managedOpenfortFactory.addStake{value: 10000000000000000}(99); + // openfortFactory.addStake{value: 10000000000000000}(99); // // expectRevert as simulateValidation() always reverts // vm.expectRevert(); @@ -995,11 +995,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Try to upgrade vm.expectRevert("Ownable: caller is not the owner"); - managedOpenfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); + openfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); // Finally upgrade vm.prank(factoryAdmin); - managedOpenfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); + openfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); // Try to use the old and new implementation before upgrade (should always behave with current values) vm.expectRevert("disabled!"); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 233d25e..e933f1e 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -8,6 +8,7 @@ import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/E import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC20} from "contracts/mock/MockERC20.sol"; import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; +import {IUpgradeableOpenfortAccount} from "contracts/interfaces/IUpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {UpgradeableOpenfortProxy} from "contracts/core/upgradeable/UpgradeableOpenfortProxy.sol"; @@ -18,7 +19,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; UpgradeableOpenfortAccount public upgradeableOpenfortAccountImpl; - UpgradeableOpenfortFactory public upgradeableOpenfortFactory; + UpgradeableOpenfortFactory public openfortFactory; /** * @notice Initialize the UpgradeableOpenfortAccount testing contract. @@ -55,7 +56,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { emit AccountImplementationDeployed(factoryAdmin); upgradeableOpenfortAccountImpl = new UpgradeableOpenfortAccount{salt: versionSalt}(); // deploy upgradeable account factory - upgradeableOpenfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( + openfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( factoryAdmin, address(entryPoint), address(upgradeableOpenfortAccountImpl), @@ -67,7 +68,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); // Create an upgradeable account wallet and get its address - accountAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); + accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1"); // deploy a new TestCounter testCounter = new TestCounter(); @@ -98,7 +99,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testCreateAccountWithNonceViaFactory() public { // Get the counterfactual address vm.prank(factoryAdmin); - address accountAddress2 = upgradeableOpenfortFactory.getAddressWithNonce(accountAdmin, "2"); + address accountAddress2 = openfortFactory.getAddressWithNonce(accountAdmin, "2"); // Expect that we will see an event containing the account and admin vm.expectEmit(true, true, false, true); @@ -106,15 +107,15 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Deploy a upgradeable account to the counterfactual address vm.prank(factoryAdmin); - upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); + openfortFactory.createAccountWithNonce(accountAdmin, "2"); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); + openfortFactory.createAccountWithNonce(accountAdmin, "2"); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); - assertEq(accountAddress2, upgradeableOpenfortFactory.getAddressWithNonce(accountAdmin, "2")); + assertEq(accountAddress2, openfortFactory.getAddressWithNonce(accountAdmin, "2")); } /* @@ -123,7 +124,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testFuzzCreateAccountWithNonceViaFactory(address _adminAddress, bytes32 _nonce) public { // Get the counterfactual address vm.prank(factoryAdmin); - address accountAddress2 = upgradeableOpenfortFactory.getAddressWithNonce(_adminAddress, _nonce); + address accountAddress2 = openfortFactory.getAddressWithNonce(_adminAddress, _nonce); // Expect that we will see an event containing the account and admin vm.expectEmit(true, true, false, true); @@ -131,15 +132,15 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Deploy a upgradeable account to the counterfactual address vm.prank(factoryAdmin); - upgradeableOpenfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - upgradeableOpenfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); - assertEq(accountAddress2, upgradeableOpenfortFactory.getAddressWithNonce(_adminAddress, _nonce)); + assertEq(accountAddress2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); } /* @@ -152,12 +153,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // revert(); // Make sure the smart account does not have any code yet - address account2 = upgradeableOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); + address account2 = openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); assertEq(account2.code.length, 0); bytes memory initCallData = abi.encodeWithSignature("createAccountWithNonce(address,bytes32)", accountAdmin, bytes32("2")); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(upgradeableOpenfortFactory)), initCallData); + bytes memory initCode = abi.encodePacked(abi.encodePacked(address(openfortFactory)), initCallData); UserOperation[] memory userOpCreateAccount = _setupUserOpExecute(account2, accountAdminPKey, initCode, address(0), 0, bytes("")); @@ -172,7 +173,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assert(account2.code.length > 0); // Make sure the counterfactual address has not been altered - assertEq(account2, upgradeableOpenfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); + assertEq(account2, openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); } /* @@ -1018,7 +1019,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testUpdateEntryPoint() public { address oldEntryPoint = address(0x0576a174D229E3cFA37253523E645A78A0C91B57); address newEntryPoint = vm.envAddress("ENTRY_POINT_ADDRESS"); - UpgradeableOpenfortFactory upgradeableOpenfortFactoryOld = new UpgradeableOpenfortFactory{salt: versionSalt}( + UpgradeableOpenfortFactory openfortFactoryOld = new UpgradeableOpenfortFactory{salt: versionSalt}( factoryAdmin, oldEntryPoint, address(upgradeableOpenfortAccountImpl), @@ -1030,8 +1031,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); // Create an upgradeable account wallet using the old EntryPoint and get its address - address payable accountOld = payable(upgradeableOpenfortFactoryOld.createAccountWithNonce(accountAdmin, "999")); - UpgradeableOpenfortAccount upgradeableAccount = UpgradeableOpenfortAccount(accountOld); + address payable oldAccountAddress = payable(openfortFactoryOld.createAccountWithNonce(accountAdmin, "999")); + IUpgradeableOpenfortAccount upgradeableAccount = IUpgradeableOpenfortAccount(oldAccountAddress); assertEq(address(upgradeableAccount.entryPoint()), oldEntryPoint); vm.expectRevert("Ownable: caller is not the owner"); From fd95e3237950ae4f6ec49adabb2d89bfdc4711f9 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 17:24:02 +0100 Subject: [PATCH 38/83] Naming convention _args --- contracts/core/managed/ManagedOpenfortProxy.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/core/managed/ManagedOpenfortProxy.sol b/contracts/core/managed/ManagedOpenfortProxy.sol index 2e1df4f..c684161 100644 --- a/contracts/core/managed/ManagedOpenfortProxy.sol +++ b/contracts/core/managed/ManagedOpenfortProxy.sol @@ -10,7 +10,7 @@ import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol" * - BeaconProxy */ contract ManagedOpenfortProxy is BeaconProxy { - constructor(address beacon, bytes memory data) BeaconProxy(beacon, data) {} + constructor(address _beaconAddress, bytes memory _data) BeaconProxy(_beaconAddress, _data) {} function implementation() external view returns (address) { return _implementation(); From d3350c956ca761056934fbfab87c8b36169dca9c Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 22 Nov 2023 18:33:09 +0100 Subject: [PATCH 39/83] Improving checks and added testAddDeposit() test --- contracts/core/base/BaseOpenfortAccount.sol | 3 +- contracts/core/base/BaseOpenfortFactory.sol | 8 +- .../core/erc6551/ERC6551OpenfortAccount.sol | 3 +- .../core/managed/ManagedOpenfortFactory.sol | 2 +- .../UpgradeableOpenfortAccount.sol | 6 +- .../UpgradeableOpenfortFactory.sol | 3 +- contracts/interfaces/IBaseOpenfortAccount.sol | 6 +- contracts/interfaces/IBaseOpenfortFactory.sol | 3 + .../interfaces/OpenfortErrorsAndEvents.sol | 3 + test/foundry/core/OpenfortBaseTest.t.sol | 6 + .../erc6551/EIP6551OpenfortBenchmark.t.sol | 4 +- .../managed/ManagedOpenfortAccountTest.t.sol | 48 ++++---- .../UpgradeableOpenfortAccountTest.t.sol | 113 +++++++++++++----- 13 files changed, 137 insertions(+), 71 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 9369e54..f387bfa 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -176,7 +176,7 @@ abstract contract BaseOpenfortAccount is /** * Execute a transaction (called directly from owner, or by entryPoint) */ - function execute(address dest, uint256 value, bytes calldata func) public virtual { + function execute(address dest, uint256 value, bytes calldata func) public payable virtual { _requireFromEntryPointOrOwner(); _call(dest, value, func); } @@ -186,6 +186,7 @@ abstract contract BaseOpenfortAccount is */ function executeBatch(address[] calldata _target, uint256[] calldata _value, bytes[] calldata _calldata) public + payable virtual { _requireFromEntryPointOrOwner(); diff --git a/contracts/core/base/BaseOpenfortFactory.sol b/contracts/core/base/BaseOpenfortFactory.sol index 5ff0586..bcd3b15 100644 --- a/contracts/core/base/BaseOpenfortFactory.sol +++ b/contracts/core/base/BaseOpenfortFactory.sol @@ -34,12 +34,8 @@ abstract contract BaseOpenfortFactory is IBaseOpenfortFactory, Ownable2Step { uint256 _lockPeriod, address _openfortGuardian ) { - if ( - _owner == address(0) || _entrypoint == address(0) || _accountImplementation == address(0) - || _openfortGuardian == address(0) - ) { - revert ZeroAddressNotAllowed(); - } + if (_owner == address(0) || _openfortGuardian == address(0)) revert ZeroAddressNotAllowed(); + if (!Address.isContract(_entrypoint) || !Address.isContract(_accountImplementation)) revert NotAContract(); if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { revert InsecurePeriod(); } diff --git a/contracts/core/erc6551/ERC6551OpenfortAccount.sol b/contracts/core/erc6551/ERC6551OpenfortAccount.sol index 76d98eb..f8c91bd 100644 --- a/contracts/core/erc6551/ERC6551OpenfortAccount.sol +++ b/contracts/core/erc6551/ERC6551OpenfortAccount.sol @@ -92,7 +92,7 @@ contract ERC6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 /** * Execute a transaction (called directly from owner, or by entryPoint) */ - function execute(address _dest, uint256 _value, bytes calldata _func) public override { + function execute(address _dest, uint256 _value, bytes calldata _func) public payable override { ++state; super.execute(_dest, _value, _func); } @@ -102,6 +102,7 @@ contract ERC6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 */ function executeBatch(address[] calldata _target, uint256[] calldata _value, bytes[] calldata _calldata) public + payable override { state += _target.length; diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index 11427b2..2d81bec 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -101,7 +101,7 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { * - `newImplementation` must be a contract. */ function _setImplementation(address newImplementation) private { - require(Address.isContract(newImplementation), "ManagedOpenfortFactory: implementation is not a contract"); + if (!Address.isContract(newImplementation)) revert NotAContract(); _implementation = newImplementation; } } diff --git a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol index 40cd6c4..e1b74ed 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -// Base account contract to inherit from and EntryPoint interface -import {BaseRecoverableAccount, IEntryPoint} from "../base/BaseRecoverableAccount.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {BaseRecoverableAccount, IEntryPoint} from "../base/BaseRecoverableAccount.sol"; /** * @title UpgradeableOpenfortAccount @@ -26,7 +26,7 @@ contract UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { * Update the EntryPoint address */ function updateEntryPoint(address _newEntrypoint) external onlyOwner { - if (_newEntrypoint == address(0)) revert ZeroAddressNotAllowed(); + if (!Address.isContract(_newEntrypoint)) revert NotAContract(); emit EntryPointUpdated(entrypointContract, _newEntrypoint); entrypointContract = _newEntrypoint; } diff --git a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol index 3f0944d..c691649 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol @@ -2,14 +2,13 @@ pragma solidity =0.8.19; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; -import {UpgradeableOpenfortAccount, IEntryPoint} from "./UpgradeableOpenfortAccount.sol"; +import {UpgradeableOpenfortAccount} from "./UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortProxy} from "./UpgradeableOpenfortProxy.sol"; import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; /** * @title UpgradeableOpenfortFactory (Non-upgradeable) * @notice Contract to create an on-chain factory to deploy new UpgradeableOpenfortAccounts. - * It uses OpenZeppelin's Create2 and UpgradeableOpenfortProxy libraries. * It inherits from: * - BaseOpenfortFactory */ diff --git a/contracts/interfaces/IBaseOpenfortAccount.sol b/contracts/interfaces/IBaseOpenfortAccount.sol index 5ed838d..1d41439 100644 --- a/contracts/interfaces/IBaseOpenfortAccount.sol +++ b/contracts/interfaces/IBaseOpenfortAccount.sol @@ -36,8 +36,10 @@ interface IBaseOpenfortAccount is IAccount { uint256[] memory extensions ); function entryPoint() external view returns (address); - function execute(address dest, uint256 value, bytes memory func) external; - function executeBatch(address[] memory _target, uint256[] memory _value, bytes[] memory _calldata) external; + function execute(address dest, uint256 value, bytes memory func) external payable; + function executeBatch(address[] memory _target, uint256[] memory _value, bytes[] memory _calldata) + external + payable; function getDeposit() external view returns (uint256); function getNonce() external view returns (uint256); function isValidSessionKey(address _sessionKey, bytes memory _callData) external returns (bool); diff --git a/contracts/interfaces/IBaseOpenfortFactory.sol b/contracts/interfaces/IBaseOpenfortFactory.sol index 05ab6ad..b503d50 100644 --- a/contracts/interfaces/IBaseOpenfortFactory.sol +++ b/contracts/interfaces/IBaseOpenfortFactory.sol @@ -8,6 +8,9 @@ interface IBaseOpenfortFactory { /// @notice Error when an address parameter is 0. error ZeroAddressNotAllowed(); + /// @notice Error when an address is not a contract. + error NotAContract(); + /// @notice Deploys a new Account for admin. function createAccountWithNonce(address _admin, bytes32 _nonce) external returns (address account); diff --git a/contracts/interfaces/OpenfortErrorsAndEvents.sol b/contracts/interfaces/OpenfortErrorsAndEvents.sol index fada2cf..9c438d0 100644 --- a/contracts/interfaces/OpenfortErrorsAndEvents.sol +++ b/contracts/interfaces/OpenfortErrorsAndEvents.sol @@ -11,6 +11,9 @@ interface OpenfortErrorsAndEvents { /// @notice Error when a function requires msg.value to be different than owner() error OwnerNotAllowed(); + /// @notice Error when an address is not a contract. + error NotAContract(); + error ZeroAddressNotAllowed(); error NotOwnerOrEntrypoint(); error NotOwner(); diff --git a/test/foundry/core/OpenfortBaseTest.t.sol b/test/foundry/core/OpenfortBaseTest.t.sol index 9dc0a73..d9ba918 100644 --- a/test/foundry/core/OpenfortBaseTest.t.sol +++ b/test/foundry/core/OpenfortBaseTest.t.sol @@ -150,4 +150,10 @@ contract OpenfortBaseTest is Test { return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); } + + /** + * AA events + */ + + event Deposited(address indexed account, uint256 totalDeposit); } diff --git a/test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol b/test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol index 371c226..363a800 100644 --- a/test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol +++ b/test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol @@ -321,7 +321,7 @@ import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAcco // abi.encodeWithSignature("execute(address,uint256,bytes)", _target, 0, _callData) // ); -// entryPoint.depositTo{value: 1000000000000000000}(address(erc6551OpenfortAccount)); +// entryPoint.depositTo{value: 1 ether}(address(erc6551OpenfortAccount)); // vm.expectRevert(); // entryPoint.simulateValidation(userOp[0]); @@ -591,7 +591,7 @@ import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAcco // ) // ); -// entryPoint.depositTo{value: 1000000000000000000}(upgradeableOpenfortAddressComplex); +// entryPoint.depositTo{value: 1 ether}(upgradeableOpenfortAddressComplex); // vm.expectRevert(); // entryPoint.simulateValidation(userOp[0]); // entryPoint.handleOps(userOp, beneficiary); diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index f939529..dcb551e 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -200,7 +200,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -231,7 +231,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecuteBatch(accountAddress, accountAdminPKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -255,7 +255,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -285,7 +285,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -321,7 +321,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -333,7 +333,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -370,7 +370,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -382,7 +382,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -408,7 +408,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -434,7 +434,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); vm.expectRevert(); @@ -464,7 +464,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -494,7 +494,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -503,7 +503,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -543,7 +543,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -573,7 +573,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -602,7 +602,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -630,7 +630,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -676,7 +676,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -722,7 +722,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -751,7 +751,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -796,7 +796,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { callData ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -863,7 +863,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { accountAddress, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -892,7 +892,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -944,7 +944,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") // ); - // entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + // entryPoint.depositTo{value: 1 ether}(accountAddress); // // Expect the simulateValidation() to always revert // vm.expectRevert(); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index e933f1e..55fe140 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -9,6 +9,7 @@ import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC20} from "contracts/mock/MockERC20.sol"; import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; import {IUpgradeableOpenfortAccount} from "contracts/interfaces/IUpgradeableOpenfortAccount.sol"; +import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {UpgradeableOpenfortProxy} from "contracts/core/upgradeable/UpgradeableOpenfortProxy.sol"; @@ -127,8 +128,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address accountAddress2 = openfortFactory.getAddressWithNonce(_adminAddress, _nonce); // Expect that we will see an event containing the account and admin - vm.expectEmit(true, true, false, true); - emit AccountCreated(accountAddress2, _adminAddress); + if (_adminAddress == address(0)) { + vm.expectRevert(); + } else { + vm.expectEmit(true, true, false, true); + emit AccountCreated(accountAddress2, _adminAddress); + } // Deploy a upgradeable account to the counterfactual address vm.prank(factoryAdmin); @@ -205,7 +210,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -236,13 +241,43 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecuteBatch(accountAddress, accountAdminPKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); // Verify that the counter has increased - assertEq(testCounter.counters(accountAddress), 3); + assertEq(testCounter.counters(accountAddress), count); + } + + /* + * Use the executeBatch() with a too big number + */ + function testFailBatchingBig() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + uint256 count = 10; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + for (uint256 i = 0; i < count; i += 1) { + targets[i] = address(testCounter); + values[i] = 0; + callData[i] = abi.encodeWithSignature("count()"); + } + + UserOperation[] memory userOp = + _setupUserOpExecuteBatch(accountAddress, accountAdminPKey, bytes(""), targets, values, callData); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + // Verify that the counter has increased + assertEq(testCounter.counters(accountAddress), count); } /* @@ -260,7 +295,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -290,7 +325,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -326,7 +361,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -338,7 +373,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -375,7 +410,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -387,7 +422,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -413,7 +448,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -440,7 +475,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { abi.encodeWithSignature("registerSessionKey(address,uint48,uint48,uint48)", sessionKey, 0, 2 ** 48 - 1, 10) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -452,7 +487,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -480,7 +515,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { abi.encodeWithSignature("registerSessionKey(address,uint48,uint48)", sessionKeyAttack, 0, 2 ** 48 - 1) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -506,7 +541,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); vm.expectRevert(); @@ -536,7 +571,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -566,7 +601,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -575,7 +610,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -615,7 +650,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -645,7 +680,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -674,7 +709,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -702,7 +737,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -748,7 +783,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -777,7 +812,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -822,7 +857,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { callData ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -888,7 +923,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -917,7 +952,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { abi.encodeWithSignature("mint(address,uint256)", beneficiary, 1) ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); entryPoint.handleOps(userOp, beneficiary); @@ -969,7 +1004,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); - entryPoint.depositTo{value: 1000000000000000000}(accountAddress); + entryPoint.depositTo{value: 1 ether}(accountAddress); // Expect the simulateValidation() to always revert vm.expectRevert(); entryPoint.simulateValidation(userOp[0]); @@ -1017,7 +1052,9 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * 2- Inform the account of the new EntryPoint by calling updateEntryPoint() */ function testUpdateEntryPoint() public { + bytes memory code = address(entryPoint).code; address oldEntryPoint = address(0x0576a174D229E3cFA37253523E645A78A0C91B57); + vm.etch(oldEntryPoint, code); // Simulate there's code here address newEntryPoint = vm.envAddress("ENTRY_POINT_ADDRESS"); UpgradeableOpenfortFactory openfortFactoryOld = new UpgradeableOpenfortFactory{salt: versionSalt}( factoryAdmin, @@ -1038,6 +1075,10 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); upgradeableAccount.updateEntryPoint(newEntryPoint); + vm.prank(accountAdmin); + vm.expectRevert(OpenfortErrorsAndEvents.NotAContract.selector); + upgradeableAccount.updateEntryPoint(address(0)); + vm.prank(accountAdmin); upgradeableAccount.updateEntryPoint(newEntryPoint); @@ -1117,6 +1158,20 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(valid, MAGICVALUE); // SHOULD PASS } + function testAddDeposit() public { + IBaseRecoverableAccount account = IBaseRecoverableAccount(payable(accountAddress)); + assertEq(0, account.getDeposit()); + vm.expectEmit(true, true, false, true); + emit Deposited(accountAddress, 1 ether); + account.addDeposit{value: 1 ether}(); + assertEq(1 ether, account.getDeposit()); + + // Make the admin of the upgradeable account wallet (deployer) call "count" + vm.prank(accountAdmin); + account.execute{value: 1 ether}(accountAddress, 1 ether, abi.encodeWithSignature("addDeposit()")); + assertEq(2 ether, account.getDeposit()); + } + /** * Lock tests * */ From 0ffc5ee38c6710bdbc89c6ff1ec378fb6b7fd7d1 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Thu, 23 Nov 2023 18:04:00 +0100 Subject: [PATCH 40/83] Removing addDeposit and withdrawDepositTo. + tests --- contracts/core/base/BaseOpenfortAccount.sol | 18 --- .../mock/MockV2UpgradeableOpenfortAccount.sol | 7 +- .../managed/ManagedOpenfortAccountTest.t.sol | 89 ++++++++++--- .../UpgradeableOpenfortAccountTest.t.sol | 118 +++++++++++++++--- 4 files changed, 180 insertions(+), 52 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index f387bfa..83703d2 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -202,24 +202,6 @@ abstract contract BaseOpenfortAccount is } } - /** - * Deposit funds for this account in the EntryPoint - */ - function addDeposit() external payable virtual { - entryPoint().depositTo{value: msg.value}(address(this)); - } - - /** - * Withdraw value from the account's deposit - * @param _withdrawAddress target to send to - * @param _amount to withdraw - * @notice ONLY the owner can call this function (it's not using _requireFromEntryPointOrOwner()) - */ - function withdrawDepositTo(address payable _withdrawAddress, uint256 _amount) external virtual { - _requireFromOwner(); - entryPoint().withdrawTo(_withdrawAddress, _amount); - } - /** * Register a session key to the account * @param _key session key to register diff --git a/contracts/mock/MockV2UpgradeableOpenfortAccount.sol b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol index 3dfb55b..ffdbf12 100644 --- a/contracts/mock/MockV2UpgradeableOpenfortAccount.sol +++ b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol @@ -25,10 +25,9 @@ contract MockV2UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgrade /** * Update the EntryPoint address */ - function updateEntryPoint(address _newEntrypoint) external onlyOwner { - if (_newEntrypoint == address(0)) revert ZeroAddressNotAllowed(); - emit EntryPointUpdated(entrypointContract, _newEntrypoint); - entrypointContract = _newEntrypoint; + function updateEntryPoint(address _newEntrypoint) external view onlyOwner { + (_newEntrypoint); + revert("disabled!"); } /** diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index dcb551e..1378bd1 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -70,10 +70,44 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.stopPrank(); } + /* + * Should be able to stake and unstake + */ + function testStakeFactory() public { + vm.expectRevert("Ownable: caller is not the owner"); + openfortFactory.addStake{value: 1 ether}(10); + + vm.expectRevert("no stake specified"); + vm.prank(factoryAdmin); + openfortFactory.addStake(10); + + vm.prank(factoryAdmin); + openfortFactory.addStake{value: 1 ether}(10); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortFactory.unlockStake(); + + vm.prank(factoryAdmin); + openfortFactory.unlockStake(); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortFactory.withdrawStake(payable(factoryAdmin)); + + vm.expectRevert("Stake withdrawal is not due"); + vm.prank(factoryAdmin); + openfortFactory.withdrawStake(payable(factoryAdmin)); + + skip(11); + + vm.prank(factoryAdmin); + openfortFactory.withdrawStake(payable(factoryAdmin)); + } + /* * Should not be able to initialize the implementation */ function testInitializeImplementation() public { + assertEq(openfortFactory.implementation(), address(managedOpenfortAccountImpl)); vm.expectRevert("Initializable: contract is already initialized"); managedOpenfortAccountImpl.initialize( accountAdmin, @@ -120,20 +154,23 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address account2 = openfortFactory.getAddressWithNonce(_adminAddress, _nonce); // Expect that we will see an event containing the account and admin - vm.expectEmit(true, true, false, true); - emit AccountCreated(account2, _adminAddress); - - // Deploy a managed account to the counterfactual address - vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); - - // Calling it again should just return the address and not create another account - vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); - - // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); - assertEq(account2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); + if (_adminAddress == address(0)) { + vm.expectRevert(); + } else { + vm.expectEmit(true, true, false, true); + emit AccountCreated(account2, _adminAddress); + // Deploy a managed account to the counterfactual address + vm.prank(factoryAdmin); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + + // Calling it again should just return the address and not create another account + vm.prank(factoryAdmin); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + + // Make sure the counterfactual address has not been altered + vm.prank(factoryAdmin); + assertEq(account2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); + } } /* @@ -686,7 +723,8 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { } /* - * Use a sessionKey with whitelisting to call ExecuteBatch(). + * Use a sessionKey with whitelisting to call ExecuteBatch() with 11 actions. + * Fail due to too much actions. */ function testFailIncrementCounterViaSessionKeyWhitelistingBatch() public { // Verify that the counter is still set to 0 @@ -731,6 +769,27 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); } + /* + * Use a sessionKey with whitelisting to call ExecuteBatch() with 11 whitelisted addresses. + * Fail due to too much whitelisted addresses. + */ + function testTooManyWhitelist() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + address sessionKey; + uint256 sessionKeyPrivKey; + (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + + address[] memory whitelist = new address[](11); + for (uint256 i = 0; i < whitelist.length; i++) { + whitelist[i] = address(testCounter); + } + vm.expectRevert("Whitelist too big"); + vm.prank(accountAdmin); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); + } + /* * Should fail, try to use a sessionKey with invalid whitelisting to call Execute(). */ diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 55fe140..492ae87 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -78,10 +78,44 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.stopPrank(); } + /* + * Should be able to stake and unstake + */ + function testStakeFactory() public { + vm.expectRevert("Ownable: caller is not the owner"); + openfortFactory.addStake{value: 1 ether}(10); + + vm.expectRevert("no stake specified"); + vm.prank(factoryAdmin); + openfortFactory.addStake(10); + + vm.prank(factoryAdmin); + openfortFactory.addStake{value: 1 ether}(10); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortFactory.unlockStake(); + + vm.prank(factoryAdmin); + openfortFactory.unlockStake(); + + vm.expectRevert("Ownable: caller is not the owner"); + openfortFactory.withdrawStake(payable(factoryAdmin)); + + vm.expectRevert("Stake withdrawal is not due"); + vm.prank(factoryAdmin); + openfortFactory.withdrawStake(payable(factoryAdmin)); + + skip(11); + + vm.prank(factoryAdmin); + openfortFactory.withdrawStake(payable(factoryAdmin)); + } + /* * Should not be able to initialize the implementation */ function testInitializeImplementation() public { + assertEq(openfortFactory.implementation(), address(upgradeableOpenfortAccountImpl)); vm.expectRevert("Initializable: contract is already initialized"); upgradeableOpenfortAccountImpl.initialize( accountAdmin, @@ -133,19 +167,18 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { } else { vm.expectEmit(true, true, false, true); emit AccountCreated(accountAddress2, _adminAddress); - } - // Deploy a upgradeable account to the counterfactual address - vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + // Deploy a upgradeable account to the counterfactual address + vm.prank(factoryAdmin); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); - // Calling it again should just return the address and not create another account - vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); - - // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); - assertEq(accountAddress2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); + // Calling it again should just return the address and not create another account + vm.prank(factoryAdmin); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + // Make sure the counterfactual address has not been altered + vm.prank(factoryAdmin); + assertEq(accountAddress2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); + } } /* @@ -866,6 +899,27 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); } + /* + * Use a sessionKey with whitelisting to call ExecuteBatch() with 11 whitelisted addresses. + * Fail due to too much whitelisted addresses. + */ + function testTooManyWhitelist() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + address sessionKey; + uint256 sessionKeyPrivKey; + (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + + address[] memory whitelist = new address[](11); + for (uint256 i = 0; i < whitelist.length; i++) { + whitelist[i] = address(testCounter); + } + vm.expectRevert("Whitelist too big"); + vm.prank(accountAdmin); + UpgradeableOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); + } + /* * Change the owner of an account and call TestCounter directly. * Important use-case: @@ -1042,6 +1096,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(MockV2UpgradeableOpenfortAccount(payable(accountAddress)).easterEgg(), 42); + vm.expectRevert("Ownable: caller is not the owner"); + IUpgradeableOpenfortAccount(payable(accountAddress)).updateEntryPoint(beneficiary); + + vm.expectRevert("disabled!"); + vm.prank(accountAdmin); + IUpgradeableOpenfortAccount(payable(accountAddress)).updateEntryPoint(beneficiary); + // Printing account address and the implementation address. Impl address should have changed console.log("Account address (proxy): ", accountAddress); console.log("Implementation address (new): ", p.implementation()); @@ -1159,17 +1220,44 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { } function testAddDeposit() public { + IBaseRecoverableAccount account = IBaseRecoverableAccount(payable(accountAddress)); + assertEq(0, account.getDeposit()); + // The commented code below is because addDeposit() was removed + // vm.expectEmit(true, true, false, true); + // emit Deposited(accountAddress, 1 ether); + // account.addDeposit{value: 1 ether}(); + // assertEq(1 ether, account.getDeposit()); + + // Make the admin of the upgradeable account wallet (deployer) call "depositTo()" + vm.expectEmit(true, true, false, true); + emit Deposited(accountAddress, 1 ether); // Notice 2 ether is the current deposit, not the deposited + vm.prank(accountAdmin); + account.execute{value: 1 ether}( + address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) + ); + assertEq(1 ether, account.getDeposit()); + } + + function testWithdrawDeposit() public { IBaseRecoverableAccount account = IBaseRecoverableAccount(payable(accountAddress)); assertEq(0, account.getDeposit()); vm.expectEmit(true, true, false, true); emit Deposited(accountAddress, 1 ether); - account.addDeposit{value: 1 ether}(); + // Make the admin of the upgradeable account wallet (deployer) call "depositTo()" + vm.prank(accountAdmin); + account.execute{value: 1 ether}( + address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) + ); assertEq(1 ether, account.getDeposit()); - // Make the admin of the upgradeable account wallet (deployer) call "count" vm.prank(accountAdmin); - account.execute{value: 1 ether}(accountAddress, 1 ether, abi.encodeWithSignature("addDeposit()")); - assertEq(2 ether, account.getDeposit()); + account.execute{value: 1 ether}( + address(entryPoint), + 0, + abi.encodeWithSignature("withdrawTo(address,uint256)", payable(accountAddress), 1 ether) + ); + assertEq(0 ether, account.getDeposit()); + assertEq(2 ether, accountAddress.balance); // Notice the Ether went to the account, not the EOA } /** From 09dbcb171653e4dbe75496f982a141888ed8d79c Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Thu, 23 Nov 2023 19:10:19 +0100 Subject: [PATCH 41/83] Moar tests --- .../UpgradeableOpenfortAccount.sol | 18 +++++++-------- .../mock/MockV2UpgradeableOpenfortAccount.sol | 22 +++++++++---------- .../managed/ManagedOpenfortAccountTest.t.sol | 2 ++ .../UpgradeableOpenfortAccountTest.t.sol | 5 ++--- .../paymaster/OpenfortPaymasterV2Test.t.sol | 10 ++++++++- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol index e1b74ed..b782270 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortAccount.sol @@ -13,15 +13,6 @@ import {BaseRecoverableAccount, IEntryPoint} from "../base/BaseRecoverableAccoun * - UUPSUpgradeable */ contract UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { - function _authorizeUpgrade(address) internal override onlyOwner {} - - /** - * Return the current EntryPoint - */ - function entryPoint() public view override returns (IEntryPoint) { - return IEntryPoint(entrypointContract); - } - /** * Update the EntryPoint address */ @@ -30,4 +21,13 @@ contract UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { emit EntryPointUpdated(entrypointContract, _newEntrypoint); entrypointContract = _newEntrypoint; } + + /** + * Return the current EntryPoint + */ + function entryPoint() public view override returns (IEntryPoint) { + return IEntryPoint(entrypointContract); + } + + function _authorizeUpgrade(address) internal override onlyOwner {} } diff --git a/contracts/mock/MockV2UpgradeableOpenfortAccount.sol b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol index ffdbf12..d7ace0a 100644 --- a/contracts/mock/MockV2UpgradeableOpenfortAccount.sol +++ b/contracts/mock/MockV2UpgradeableOpenfortAccount.sol @@ -13,20 +13,11 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U * - UUPSUpgradeable */ contract MockV2UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgradeable { - function _authorizeUpgrade(address) internal override onlyOwner {} - - /** - * Return the modified EntryPoint - */ - function entryPoint() public pure override returns (IEntryPoint) { - return IEntryPoint(0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF); - } - /** * Update the EntryPoint address */ - function updateEntryPoint(address _newEntrypoint) external view onlyOwner { - (_newEntrypoint); + function updateEntryPoint(address _newEntrypoint) external { + _authorizeUpgrade(_newEntrypoint); revert("disabled!"); } @@ -36,4 +27,13 @@ contract MockV2UpgradeableOpenfortAccount is BaseRecoverableAccount, UUPSUpgrade function easterEgg() external pure returns (uint256) { return 42; } + + /** + * Return the modified EntryPoint + */ + function entryPoint() public pure override returns (IEntryPoint) { + return IEntryPoint(0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF); + } + + function _authorizeUpgrade(address) internal override {} } diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 1378bd1..5cc420a 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -156,6 +156,8 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the account and admin if (_adminAddress == address(0)) { vm.expectRevert(); + vm.prank(factoryAdmin); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); } else { vm.expectEmit(true, true, false, true); emit AccountCreated(account2, _adminAddress); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 492ae87..f2f4d49 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -164,6 +164,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the account and admin if (_adminAddress == address(0)) { vm.expectRevert(); + vm.prank(factoryAdmin); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce); } else { vm.expectEmit(true, true, false, true); emit AccountCreated(accountAddress2, _adminAddress); @@ -1096,9 +1098,6 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(MockV2UpgradeableOpenfortAccount(payable(accountAddress)).easterEgg(), 42); - vm.expectRevert("Ownable: caller is not the owner"); - IUpgradeableOpenfortAccount(payable(accountAddress)).updateEntryPoint(beneficiary); - vm.expectRevert("disabled!"); vm.prank(accountAdmin); IUpgradeableOpenfortAccount(payable(accountAddress)).updateEntryPoint(beneficiary); diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index 992de07..0fc1b8e 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -11,6 +11,7 @@ import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/Upgradeable import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {OpenfortPaymasterV2} from "contracts/paymaster/OpenfortPaymasterV2.sol"; import {OpenfortBaseTest} from "../core/OpenfortBaseTest.t.sol"; +import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; contract OpenfortPaymasterV2Test is OpenfortBaseTest { using ECDSA for bytes32; @@ -357,6 +358,8 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { vm.expectRevert(); openfortPaymaster.depositFor{value: 1 ether}(paymasterAdmin); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0); + // Paymaster deposits 1 ETH to EntryPoint openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); @@ -423,7 +426,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); // Only owner cannot call it - vm.expectRevert(); + vm.expectRevert("Ownable: caller is not the owner"); openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); @@ -432,6 +435,11 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 100 ether); assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + vm.expectRevert(OpenfortErrorsAndEvents.ZeroValueNotAllowed.selector); + vm.prank(paymasterAdmin); + openfortPaymaster.withdrawFromDepositor(address(0), payable(factoryAdmin), 1 ether); + assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + vm.prank(paymasterAdmin); openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); From 8de7879d4bcfa11f77711ff3a29e5d50f332a290 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Thu, 23 Nov 2023 23:07:49 +0100 Subject: [PATCH 42/83] Small improvements --- .../core/base/BaseRecoverableAccount.sol | 10 ------- .../interfaces/IBaseRecoverableAccount.sol | 1 - .../managed/ManagedOpenfortAccountTest.t.sol | 26 ++++++++++++------- .../UpgradeableOpenfortAccountTest.t.sol | 10 +------ 4 files changed, 17 insertions(+), 30 deletions(-) diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 2ddd8b3..5ea84fe 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -205,16 +205,6 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg return guardiansConfig.info[_guardian].exists; } - /** - * @notice Checks if an address is a guardian or an account authorised to sign on behalf of a smart-contract guardian. - * @param _guardian the address to test - * @return _isGuardian `true` if the address is a guardian for the wallet otherwise `false`. - */ - function isGuardianOrGuardianSigner(address _guardian) external pure returns (bool _isGuardian) { - (_guardian); - _isGuardian = false; // ToDo for smart contract wallets acting as guardians in the future - } - /** * @notice Lets the owner propose a guardian to its Openfort account. * The first guardians are added when the account is created. All following proposals must be confirmed diff --git a/contracts/interfaces/IBaseRecoverableAccount.sol b/contracts/interfaces/IBaseRecoverableAccount.sol index cc8dcca..24f9fbb 100644 --- a/contracts/interfaces/IBaseRecoverableAccount.sol +++ b/contracts/interfaces/IBaseRecoverableAccount.sol @@ -50,7 +50,6 @@ interface IBaseRecoverableAccount is IBaseOpenfortAccount { function getLock() external view returns (uint256 _releaseAfter); function guardianCount() external view returns (uint256); function isGuardian(address _guardian) external view returns (bool); - function isGuardianOrGuardianSigner(address _guardian) external pure returns (bool _isGuardian); function isLocked() external view returns (bool); function lock() external; function proposeGuardian(address _guardian) external; diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 5cc420a..e399455 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -11,6 +11,7 @@ import {ManagedOpenfortAccount} from "contracts/core/managed/ManagedOpenfortAcco import {ManagedOpenfortFactory} from "contracts/core/managed/ManagedOpenfortFactory.sol"; import {ManagedOpenfortProxy} from "contracts/core/managed/ManagedOpenfortProxy.sol"; import {MockV2ManagedOpenfortAccount} from "contracts/mock/MockV2ManagedOpenfortAccount.sol"; +import {IBaseOpenfortFactory} from "contracts/interfaces/IBaseOpenfortFactory.sol"; import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; contract ManagedOpenfortAccountTest is OpenfortBaseTest { @@ -497,6 +498,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); + vm.prank(accountAdmin); ManagedOpenfortAccount(payable(accountAddress)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( @@ -1036,7 +1038,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * 1- Deploy a new account implementation with the new EntryPoint address and disabled * 2- Upgrade the implementation address */ - function testUpgradeEntryPoint() public { + function testUpgradeBeacon() public { address newEntryPoint = 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF; // Check addressess @@ -1050,6 +1052,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(ManagedOpenfortAccount(payable(accountAddress)).getDeposit(), 0); assertEq(MockV2ManagedOpenfortAccount(payable(accountAddress)).getDeposit(), 0); + ManagedOpenfortProxy p = ManagedOpenfortProxy(payable(accountAddress)); + // Printing account address and the implementation address + console.log("Account address (proxy): ", accountAddress); + console.log("Implementation address (old): ", p.implementation()); + // Deploy the new account implementation MockV2ManagedOpenfortAccount mockV2ManagedOpenfortAccount = new MockV2ManagedOpenfortAccount{salt: versionSalt}(); @@ -1058,6 +1065,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); + vm.expectRevert(IBaseOpenfortFactory.NotAContract.selector); + vm.prank(factoryAdmin); + openfortFactory.upgradeTo(address(0)); + // Finally upgrade vm.prank(factoryAdmin); openfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); @@ -1078,6 +1089,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(); MockV2ManagedOpenfortAccount(payable(accountAddress)).getDeposit(); + // Printing account address and the implementation address + console.log("Account address (proxy): ", accountAddress); + console.log("Implementation address (new): ", p.implementation()); + // Check that the EntryPoint is now upgraded too assertEq(address(MockV2ManagedOpenfortAccount(payable(address(accountAddress))).entryPoint()), newEntryPoint); } @@ -2436,13 +2451,4 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // New owner should be now newOwner assertEq(openfortAccount.owner(), address(newOwner)); } - - /* - * Temporal test function for coverage purposes showing - * that isGuardianOrGuardianSigner() always returns false. - */ - function testStubFakeMockTempisGuardian() public { - ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); - assertEq(openfortAccount.isGuardianOrGuardianSigner(OPENFORT_GUARDIAN), false); - } } diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index f2f4d49..2f4cff3 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -600,6 +600,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(accountAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); + vm.prank(accountAdmin); IBaseRecoverableAccount(payable(accountAddress)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( @@ -2542,13 +2543,4 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // New owner should be now newOwner assertEq(openfortAccount.owner(), address(newOwner)); } - - /* - * Temporal test function for coverage purposes showing - * that isGuardianOrGuardianSigner() always returns false. - */ - function testStubFakeMockTempisGuardian() public { - IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); - assertEq(openfortAccount.isGuardianOrGuardianSigner(OPENFORT_GUARDIAN), false); - } } From ffde3c829b1928cd0cb407c6f868325b61946995 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 24 Nov 2023 14:32:58 +0100 Subject: [PATCH 43/83] Testing ERC6551 --- .../core/erc6551/ERC6551OpenfortAccount.sol | 6 +- .../core/erc6551/ERC6551OpenfortProxy.sol | 26 +- .../erc6551/EIP6551OpenfortAccountTest.t.sol | 554 +++++++++--------- 3 files changed, 295 insertions(+), 291 deletions(-) diff --git a/contracts/core/erc6551/ERC6551OpenfortAccount.sol b/contracts/core/erc6551/ERC6551OpenfortAccount.sol index f8c91bd..21ac3ad 100644 --- a/contracts/core/erc6551/ERC6551OpenfortAccount.sol +++ b/contracts/core/erc6551/ERC6551OpenfortAccount.sol @@ -113,10 +113,8 @@ contract ERC6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 * Update the EntryPoint address */ function updateEntryPoint(address _newEntrypoint) external { - if (_newEntrypoint == address(0)) { - revert ZeroAddressNotAllowed(); - } _requireFromOwner(); + if (_newEntrypoint == address(0)) revert ZeroAddressNotAllowed(); ++state; emit EntryPointUpdated(entrypointContract, _newEntrypoint); entrypointContract = _newEntrypoint; @@ -129,7 +127,7 @@ contract ERC6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 return IEntryPoint(entrypointContract); } - function supportsInterface(bytes4 _interfaceId) public pure override returns (bool) { + function supportsInterface(bytes4 _interfaceId) external pure override returns (bool) { return ( _interfaceId == type(IERC6551Account).interfaceId || _interfaceId == type(IERC6551Executable).interfaceId || _interfaceId == type(IERC1155Receiver).interfaceId || _interfaceId == type(IERC721Receiver).interfaceId diff --git a/contracts/core/erc6551/ERC6551OpenfortProxy.sol b/contracts/core/erc6551/ERC6551OpenfortProxy.sol index 446c15c..6223583 100644 --- a/contracts/core/erc6551/ERC6551OpenfortProxy.sol +++ b/contracts/core/erc6551/ERC6551OpenfortProxy.sol @@ -3,8 +3,6 @@ pragma solidity =0.8.19; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -error InvalidImplementation(); - /** * @title ERC6551OpenfortProxy (Non-upgradeable) * @notice Contract to create the ERC6551 proxies @@ -12,31 +10,9 @@ error InvalidImplementation(); * - ERC1967Proxy */ contract ERC6551OpenfortProxy is ERC1967Proxy { - address internal immutable defaultImplementation; - - constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) { - if (_logic == address(0)) revert InvalidImplementation(); - defaultImplementation = _logic; - } - - // constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) {} + constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) {} function implementation() external view returns (address) { return _implementation(); } - - function _implementation() internal view virtual override returns (address implementationAddress) { - implementationAddress = _getImplementation(); - if (implementationAddress == address(0)) return defaultImplementation; - } - - function _beforeFallback() internal virtual override { - super._beforeFallback(); - if (msg.data.length == 0) { - if (_getImplementation() == address(0)) { - _upgradeTo(defaultImplementation); - _delegate(defaultImplementation); - } - } - } } diff --git a/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol index e936c1e..9f470b4 100644 --- a/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol @@ -1,265 +1,295 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -// import {console} from "lib/forge-std/src/Test.sol"; -// import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -// import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; -// import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -// import {MockERC721} from "contracts/mock/MockERC721.sol"; -// import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; -// import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; - -// contract ERC6551OpenfortAccountTest is OpenfortBaseTest { -// using ECDSA for bytes32; - -// ERC6551Registry public erc6551Registry; -// ERC6551OpenfortAccount public erc6551OpenfortAccount; -// ERC6551OpenfortAccount public implERC6551OpenfortAccount; -// MockERC721 public mockERC721; - -// /** -// * @notice Initialize the StaticOpenfortAccount testing contract. -// * Scenario: -// * - factoryAdmin is the deployer (and owner) of the mockNFT -// * - accountAdmin is the account used to deploy new static accounts -// * - entryPoint is the singleton EntryPoint -// * - testCounter is the counter used to test userOps -// */ -// function setUp() public { -// versionSalt = bytes32(0x0); -// // Setup and fund signers -// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); -// vm.deal(factoryAdmin, 100 ether); -// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); -// vm.deal(accountAdmin, 100 ether); - -// uint256 chainId; -// assembly { -// chainId := chainid() -// } -// console.log("ChainId:", chainId); - -// vm.startPrank(factoryAdmin); - -// // If we are in a fork -// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { -// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); -// } -// // If not a fork, deploy entryPoint (at the correct address) -// else { -// EntryPoint entryPoint_aux = new EntryPoint(); -// bytes memory code = address(entryPoint_aux).code; -// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); -// vm.etch(targetAddr, code); -// entryPoint = EntryPoint(payable(targetAddr)); -// } - -// // If we are in a fork -// if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { -// erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); -// console.log("Using ERC6551 registry from a fork"); -// } -// // If not a fork, deploy ERC6551 registry (at the correct address) -// else { -// ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); -// bytes memory code = address(ERC6551Registry_aux).code; -// address targetAddr = address(vm.envAddress("ERC6551_REGISTRY_ADDRESS")); -// vm.etch(targetAddr, code); -// erc6551Registry = ERC6551Registry(payable(targetAddr)); -// } - -// // deploy a new MockERC721 collection -// mockERC721 = new MockERC721{salt: versionSalt}(); - -// implERC6551OpenfortAccount = new ERC6551OpenfortAccount{salt: versionSalt}(); - -// address erc6551OpenfortAccountAddress = erc6551Registry.createAccount( -// address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 -// ); - -// erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress)); -// console.log(erc6551OpenfortAccount.state()); -// erc6551OpenfortAccount.initialize(address(entryPoint)); -// console.log(erc6551OpenfortAccount.state()); - -// mockERC721.mint(erc6551OpenfortAccountAddress, 1); - -// vm.stopPrank(); -// } - -// /* -// * Test reinitialize. It should fail. -// */ -// function testFailReinitialize() public { -// erc6551OpenfortAccount.initialize(address(entryPoint)); -// } - -// /* -// * Test deploy. Regular, no userOps. -// */ -// function testERC6551Deploy() public { -// address deployedAccount = -// erc6551Registry.createAccount(address(implERC6551OpenfortAccount), 0, block.chainid, address(0), 0); - -// assertTrue(deployedAccount != address(0)); - -// address predictedAccount = -// erc6551Registry.account(address(implERC6551OpenfortAccount), 0, block.chainid, address(0), 0); - -// assertEq(predictedAccount, deployedAccount); -// } - -// /* -// * Test initialize implementation. It should fail. -// */ -// function testFailInitializeImplementation() public { -// implERC6551OpenfortAccount.initialize(address(entryPoint)); -// } - -// /* -// * Check implementation has not been initialized. -// * EntryPoint address should be 0. Should pass. -// */ -// function testImplementationNoEntryPointAddr() public { -// IEntryPoint e = implERC6551OpenfortAccount.entryPoint(); -// assertEq(address(e), address(0)); -// } - -// /* -// * Create a 2nd account using the same technique than in setup with a new salt (2). -// */ -// function testCreate2ndAcc() public { -// uint256 chainId; -// assembly { -// chainId := chainid() -// } -// address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( -// address(implERC6551OpenfortAccount), bytes32(0), chainId, address(mockERC721), 1 -// ); - -// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); -// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); -// assertEq(address(e), address(entryPoint)); -// assertNotEq(address(e), erc6551OpenfortAccountAddress2); -// } - -// /* -// * Create a new account using createAccount() and the initializer. -// */ -// function testCreateAccInitializer() public { -// uint256 chainId; -// assembly { -// chainId := chainid() -// } -// address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( -// address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 -// ); -// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); -// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); -// assertEq(address(e), address(entryPoint)); -// } - -// /* -// * Create a new account using createAccount() and the initializer. -// * Test initialize again should fail. -// */ -// function testFailCreateAccInitializerNoReinit() public { -// uint256 chainId; -// assembly { -// chainId := chainid() -// } -// address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( -// address(implERC6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1 -// ); - -// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); -// erc6551OpenfortAccount2.initialize(address(entryPoint)); -// } - -// /* -// * Test getDeposit() function. -// * First ERC4337 function called by this ERC6551-compatible account. -// */ -// function testGetDeposit() public { -// uint256 deposit; -// deposit = erc6551OpenfortAccount.getDeposit(); -// assertEq(deposit, 0); - -// // We can add deposit by directly calling the EntryPoint -// entryPoint.depositTo{value: 1}(address(erc6551OpenfortAccount)); -// deposit = erc6551OpenfortAccount.getDeposit(); -// assertEq(deposit, 1); - -// // We can ALSO add deposit by calling the EntryPoint addDeposit() function of the account -// erc6551OpenfortAccount.addDeposit{value: 1}(); -// deposit = erc6551OpenfortAccount.getDeposit(); -// assertEq(deposit, 2); -// } - -// /* -// * Test owner() function. -// * Check that the owner of the erc6551 account is the owner of the NFT -// */ -// function testOwner() public { -// assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); -// assertEq(erc6551OpenfortAccount.owner(), address(erc6551OpenfortAccount)); -// } - -// /* -// * Test owner() function. -// * Check that the owner of the erc6551 account is the owner of the NFT -// */ -// function testNotOwner() public { -// // Burning the NFT -// vm.prank(address(erc6551OpenfortAccount)); -// mockERC721.transferFrom(address(erc6551OpenfortAccount), address(1), 1); - -// assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); -// assertNotEq(erc6551OpenfortAccount.owner(), address(erc6551OpenfortAccount)); -// assertEq(erc6551OpenfortAccount.owner(), address(1)); -// } - -// /* -// * Create an account by directly calling the registry. -// */ -// function testCreateAccountWithNonceViaRegistry() public { -// uint256 chainId; -// assembly { -// chainId := chainid() -// } - -// // Get the counterfactual address -// vm.prank(factoryAdmin); -// address erc6551OpenfortAccountAddress2 = -// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); - -// // Expect that we will see an event containing the account and admin -// // vm.expectEmit(true, true, false, true); -// // emit IERC6551Registry.ERC6551AccountCreated( -// // erc6551OpenfortAccountAddress2, address(erc6551OpenfortAccount), chainId, address(mockERC721), 1, 2 -// // ); - -// // Deploy a static account to the counterfactual address -// vm.prank(factoryAdmin); -// erc6551Registry.createAccount(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); - -// // Make sure the counterfactual address has not been altered -// vm.prank(factoryAdmin); -// assertEq( -// erc6551OpenfortAccountAddress2, -// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) -// ); -// // assertNotEq( -// // erc6551OpenfortAccountAddress2, -// // erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) -// // ); -// assertNotEq( -// erc6551OpenfortAccountAddress2, -// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) -// ); -// assertNotEq( -// erc6551OpenfortAccountAddress2, -// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(0), 1) -// ); -// } -// } +import {console} from "lib/forge-std/src/Test.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; +import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +import {MockERC721} from "contracts/mock/MockERC721.sol"; +import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; +import {ERC6551OpenfortProxy} from "contracts/core/erc6551/ERC6551OpenfortProxy.sol"; +import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; +import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; + +contract ERC6551OpenfortAccountTest is OpenfortBaseTest { + using ECDSA for bytes32; + + ERC6551Registry public erc6551Registry; + ERC6551OpenfortAccount public erc6551OpenfortAccount; + ERC6551OpenfortAccount public erc6551OpenfortAccountImpl; + ERC6551OpenfortProxy public erc6551OpenfortAccountProxy; + MockERC721 public mockERC721; + + /** + * @notice Initialize the StaticOpenfortAccount testing contract. + * Scenario: + * - factoryAdmin is the deployer (and owner) of the mockNFT + * - accountAdmin is the account used to deploy new static accounts + * - entryPoint is the singleton EntryPoint + * - testCounter is the counter used to test userOps + */ + function setUp() public { + versionSalt = bytes32(0x0); + // Setup and fund signers + (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); + vm.deal(factoryAdmin, 100 ether); + (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); + vm.deal(accountAdmin, 100 ether); + + uint256 chainId; + assembly { + chainId := chainid() + } + console.log("ChainId:", chainId); + + vm.startPrank(factoryAdmin); + + // If we are in a fork + if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { + entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); + } + // If not a fork, deploy entryPoint (at the correct address) + else { + EntryPoint entryPoint_aux = new EntryPoint(); + bytes memory code = address(entryPoint_aux).code; + address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); + vm.etch(targetAddr, code); + entryPoint = EntryPoint(payable(targetAddr)); + } + + // If we are in a fork + if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { + erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); + console.log("Using ERC6551 registry from a fork"); + } + // If not a fork, deploy ERC6551 registry (at the correct address) + else { + ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); + bytes memory code = address(ERC6551Registry_aux).code; + address targetAddr = address(vm.envAddress("ERC6551_REGISTRY_ADDRESS")); + vm.etch(targetAddr, code); + erc6551Registry = ERC6551Registry(payable(targetAddr)); + } + + // deploy a new MockERC721 collection + mockERC721 = new MockERC721{salt: versionSalt}(); + + erc6551OpenfortAccountImpl = new ERC6551OpenfortAccount{salt: versionSalt}(); + + erc6551OpenfortAccountProxy = + new ERC6551OpenfortProxy{salt: versionSalt}(address(erc6551OpenfortAccountImpl), ""); + + erc6551OpenfortAccountProxy.implementation(); + erc6551OpenfortAccountProxy.implementation(); + erc6551OpenfortAccountProxy.implementation(); + erc6551OpenfortAccountProxy.implementation(); + + accountAddress = erc6551Registry.createAccount( + address(erc6551OpenfortAccountProxy), versionSalt, chainId, address(mockERC721), 1 + ); + + vm.stopPrank(); + + erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(accountAddress)); + vm.expectRevert(); + erc6551OpenfortAccount.initialize(address(entryPoint)); + console.log("State: ", erc6551OpenfortAccount.state()); + + vm.expectRevert(OpenfortErrorsAndEvents.NotOwner.selector); + erc6551OpenfortAccount.initialize(address(entryPoint)); + + vm.prank(factoryAdmin); + mockERC721.mint(accountAdmin, 1); + + vm.prank(accountAdmin); + erc6551OpenfortAccount.initialize(address(entryPoint)); + } + + /* + * Test get implementation from proxy. + */ + function testGetImplementation() public view { + ERC6551OpenfortProxy p = ERC6551OpenfortProxy(payable(accountAddress)); + p.implementation(); + } + + /* + * Test reinitialize. It should fail. + */ + function testFailReinitialize() public { + vm.prank(accountAdmin); + erc6551OpenfortAccount.initialize(address(entryPoint)); + } + + /* + * Test initialize implementation. It should fail. + */ + function testFailInitializeImplementation() public { + erc6551OpenfortAccountImpl.initialize(address(entryPoint)); + } + + /* + * Test deploy. Regular, no userOps. + */ + function testERC6551Deploy() public { + address deployedAccount = + erc6551Registry.createAccount(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); + + assertTrue(deployedAccount != address(0)); + + address predictedAccount = + erc6551Registry.account(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); + + assertEq(predictedAccount, deployedAccount); + } + + /* + * Check implementation has not been initialized. + * EntryPoint address should be 0. Should pass. + */ + function testImplementationNoEntryPointAddr() public { + IEntryPoint e = erc6551OpenfortAccountImpl.entryPoint(); + assertEq(address(e), address(0)); + } + + /* + * Create a 2nd account using the same technique than in setup with a new salt (2). + */ + function testCreate2ndAcc() public { + uint256 chainId; + assembly { + chainId := chainid() + } + address accountAddress2 = erc6551Registry.createAccount( + address(erc6551OpenfortAccountImpl), bytes32(0), chainId, address(mockERC721), 1 + ); + + ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); + IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); + assertEq(address(e), address(entryPoint)); + assertNotEq(address(e), accountAddress2); + } + + /* + * Create a new account using createAccount() and the initializer. + */ + function testCreateAccInitializer() public { + uint256 chainId; + assembly { + chainId := chainid() + } + address accountAddress2 = erc6551Registry.createAccount( + address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 + ); + ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); + IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); + assertEq(address(e), address(entryPoint)); + } + + /* + * Create a new account using createAccount() and the initializer. + * Test initialize again should fail. + */ + function testFailCreateAccInitializerNoReinit() public { + uint256 chainId; + assembly { + chainId := chainid() + } + address accountAddress2 = erc6551Registry.createAccount( + address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 + ); + + ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); + erc6551OpenfortAccount2.initialize(address(entryPoint)); + } + + /* + * Test getDeposit() function. + * First ERC4337 function called by this ERC6551-compatible account. + */ + function testGetDeposit() public { + uint256 deposit; + deposit = erc6551OpenfortAccount.getDeposit(); + assertEq(deposit, 0); + + // We can add deposit by directly calling the EntryPoint + entryPoint.depositTo{value: 1 ether}(address(erc6551OpenfortAccount)); + deposit = erc6551OpenfortAccount.getDeposit(); + assertEq(deposit, 1 ether); + + // We can ALSO add deposit by calling the EntryPoint depositTo() function + vm.prank(accountAdmin); + erc6551OpenfortAccount.execute{value: 1 ether}( + address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) + ); + deposit = erc6551OpenfortAccount.getDeposit(); + assertEq(deposit, 2 ether); + } + + /* + * Test owner() function. + * Check that the owner of the erc6551 account is the owner of the NFT + */ + function testOwner() public { + assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); + assertEq(erc6551OpenfortAccount.owner(), accountAdmin); + } + + /* + * Test owner() function. + * Check that the owner of the erc6551 account is the owner of the NFT + */ + function testNotOwner() public { + // Burning the NFT + vm.prank(accountAdmin); + mockERC721.transferFrom(accountAdmin, address(1), 1); + + assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); + assertNotEq(erc6551OpenfortAccount.owner(), accountAdmin); + assertEq(erc6551OpenfortAccount.owner(), address(1)); + } + + /* + * Create an account by directly calling the registry. + */ + function testCreateAccountWithNonceViaRegistry() public { + uint256 chainId; + assembly { + chainId := chainid() + } + + // Get the counterfactual address + vm.prank(factoryAdmin); + address accountAddress2 = + erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); + + // Expect that we will see an event containing the account and admin + // vm.expectEmit(true, true, false, true); + // emit IERC6551Registry.ERC6551AccountCreated( + // accountAddress2, address(erc6551OpenfortAccount), chainId, address(mockERC721), 1, 2 + // ); + + // Deploy a static account to the counterfactual address + vm.prank(factoryAdmin); + erc6551Registry.createAccount(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); + + // Make sure the counterfactual address has not been altered + vm.prank(factoryAdmin); + assertEq( + accountAddress2, + erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) + ); + // assertNotEq( + // accountAddress2, + // erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) + // ); + assertNotEq( + accountAddress2, + erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) + ); + assertNotEq( + accountAddress2, + erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(0), 1) + ); + } +} From dbdc4e91db2c66d49f0d846714d9e0fb7427ef1a Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 24 Nov 2023 14:38:14 +0100 Subject: [PATCH 44/83] Commenting out ERC6551 temp --- .../erc6551/EIP6551OpenfortAccountTest.t.sol | 584 +++++++++--------- 1 file changed, 292 insertions(+), 292 deletions(-) diff --git a/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol index 9f470b4..46d860f 100644 --- a/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol +++ b/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol @@ -1,295 +1,295 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {console} from "lib/forge-std/src/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; -import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {MockERC721} from "contracts/mock/MockERC721.sol"; -import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; -import {ERC6551OpenfortProxy} from "contracts/core/erc6551/ERC6551OpenfortProxy.sol"; -import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; -import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; - -contract ERC6551OpenfortAccountTest is OpenfortBaseTest { - using ECDSA for bytes32; - - ERC6551Registry public erc6551Registry; - ERC6551OpenfortAccount public erc6551OpenfortAccount; - ERC6551OpenfortAccount public erc6551OpenfortAccountImpl; - ERC6551OpenfortProxy public erc6551OpenfortAccountProxy; - MockERC721 public mockERC721; - - /** - * @notice Initialize the StaticOpenfortAccount testing contract. - * Scenario: - * - factoryAdmin is the deployer (and owner) of the mockNFT - * - accountAdmin is the account used to deploy new static accounts - * - entryPoint is the singleton EntryPoint - * - testCounter is the counter used to test userOps - */ - function setUp() public { - versionSalt = bytes32(0x0); - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - - uint256 chainId; - assembly { - chainId := chainid() - } - console.log("ChainId:", chainId); - - vm.startPrank(factoryAdmin); - - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at the correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } - - // If we are in a fork - if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { - erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); - console.log("Using ERC6551 registry from a fork"); - } - // If not a fork, deploy ERC6551 registry (at the correct address) - else { - ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); - bytes memory code = address(ERC6551Registry_aux).code; - address targetAddr = address(vm.envAddress("ERC6551_REGISTRY_ADDRESS")); - vm.etch(targetAddr, code); - erc6551Registry = ERC6551Registry(payable(targetAddr)); - } - - // deploy a new MockERC721 collection - mockERC721 = new MockERC721{salt: versionSalt}(); - - erc6551OpenfortAccountImpl = new ERC6551OpenfortAccount{salt: versionSalt}(); - - erc6551OpenfortAccountProxy = - new ERC6551OpenfortProxy{salt: versionSalt}(address(erc6551OpenfortAccountImpl), ""); - - erc6551OpenfortAccountProxy.implementation(); - erc6551OpenfortAccountProxy.implementation(); - erc6551OpenfortAccountProxy.implementation(); - erc6551OpenfortAccountProxy.implementation(); - - accountAddress = erc6551Registry.createAccount( - address(erc6551OpenfortAccountProxy), versionSalt, chainId, address(mockERC721), 1 - ); - - vm.stopPrank(); - - erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(accountAddress)); - vm.expectRevert(); - erc6551OpenfortAccount.initialize(address(entryPoint)); - console.log("State: ", erc6551OpenfortAccount.state()); - - vm.expectRevert(OpenfortErrorsAndEvents.NotOwner.selector); - erc6551OpenfortAccount.initialize(address(entryPoint)); - - vm.prank(factoryAdmin); - mockERC721.mint(accountAdmin, 1); - - vm.prank(accountAdmin); - erc6551OpenfortAccount.initialize(address(entryPoint)); - } - - /* - * Test get implementation from proxy. - */ - function testGetImplementation() public view { - ERC6551OpenfortProxy p = ERC6551OpenfortProxy(payable(accountAddress)); - p.implementation(); - } - - /* - * Test reinitialize. It should fail. - */ - function testFailReinitialize() public { - vm.prank(accountAdmin); - erc6551OpenfortAccount.initialize(address(entryPoint)); - } - - /* - * Test initialize implementation. It should fail. - */ - function testFailInitializeImplementation() public { - erc6551OpenfortAccountImpl.initialize(address(entryPoint)); - } - - /* - * Test deploy. Regular, no userOps. - */ - function testERC6551Deploy() public { - address deployedAccount = - erc6551Registry.createAccount(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); - - assertTrue(deployedAccount != address(0)); - - address predictedAccount = - erc6551Registry.account(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); - - assertEq(predictedAccount, deployedAccount); - } - - /* - * Check implementation has not been initialized. - * EntryPoint address should be 0. Should pass. - */ - function testImplementationNoEntryPointAddr() public { - IEntryPoint e = erc6551OpenfortAccountImpl.entryPoint(); - assertEq(address(e), address(0)); - } - - /* - * Create a 2nd account using the same technique than in setup with a new salt (2). - */ - function testCreate2ndAcc() public { - uint256 chainId; - assembly { - chainId := chainid() - } - address accountAddress2 = erc6551Registry.createAccount( - address(erc6551OpenfortAccountImpl), bytes32(0), chainId, address(mockERC721), 1 - ); - - ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); - IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); - assertEq(address(e), address(entryPoint)); - assertNotEq(address(e), accountAddress2); - } - - /* - * Create a new account using createAccount() and the initializer. - */ - function testCreateAccInitializer() public { - uint256 chainId; - assembly { - chainId := chainid() - } - address accountAddress2 = erc6551Registry.createAccount( - address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 - ); - ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); - IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); - assertEq(address(e), address(entryPoint)); - } - - /* - * Create a new account using createAccount() and the initializer. - * Test initialize again should fail. - */ - function testFailCreateAccInitializerNoReinit() public { - uint256 chainId; - assembly { - chainId := chainid() - } - address accountAddress2 = erc6551Registry.createAccount( - address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 - ); - - ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); - erc6551OpenfortAccount2.initialize(address(entryPoint)); - } - - /* - * Test getDeposit() function. - * First ERC4337 function called by this ERC6551-compatible account. - */ - function testGetDeposit() public { - uint256 deposit; - deposit = erc6551OpenfortAccount.getDeposit(); - assertEq(deposit, 0); - - // We can add deposit by directly calling the EntryPoint - entryPoint.depositTo{value: 1 ether}(address(erc6551OpenfortAccount)); - deposit = erc6551OpenfortAccount.getDeposit(); - assertEq(deposit, 1 ether); - - // We can ALSO add deposit by calling the EntryPoint depositTo() function - vm.prank(accountAdmin); - erc6551OpenfortAccount.execute{value: 1 ether}( - address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) - ); - deposit = erc6551OpenfortAccount.getDeposit(); - assertEq(deposit, 2 ether); - } - - /* - * Test owner() function. - * Check that the owner of the erc6551 account is the owner of the NFT - */ - function testOwner() public { - assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); - assertEq(erc6551OpenfortAccount.owner(), accountAdmin); - } - - /* - * Test owner() function. - * Check that the owner of the erc6551 account is the owner of the NFT - */ - function testNotOwner() public { - // Burning the NFT - vm.prank(accountAdmin); - mockERC721.transferFrom(accountAdmin, address(1), 1); - - assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); - assertNotEq(erc6551OpenfortAccount.owner(), accountAdmin); - assertEq(erc6551OpenfortAccount.owner(), address(1)); - } - - /* - * Create an account by directly calling the registry. - */ - function testCreateAccountWithNonceViaRegistry() public { - uint256 chainId; - assembly { - chainId := chainid() - } - - // Get the counterfactual address - vm.prank(factoryAdmin); - address accountAddress2 = - erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); - - // Expect that we will see an event containing the account and admin - // vm.expectEmit(true, true, false, true); - // emit IERC6551Registry.ERC6551AccountCreated( - // accountAddress2, address(erc6551OpenfortAccount), chainId, address(mockERC721), 1, 2 - // ); - - // Deploy a static account to the counterfactual address - vm.prank(factoryAdmin); - erc6551Registry.createAccount(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); - - // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); - assertEq( - accountAddress2, - erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) - ); - // assertNotEq( - // accountAddress2, - // erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) - // ); - assertNotEq( - accountAddress2, - erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) - ); - assertNotEq( - accountAddress2, - erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(0), 1) - ); - } -} +// import {console} from "lib/forge-std/src/Test.sol"; +// import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +// import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; +// import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +// import {MockERC721} from "contracts/mock/MockERC721.sol"; +// import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; +// import {ERC6551OpenfortProxy} from "contracts/core/erc6551/ERC6551OpenfortProxy.sol"; +// import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; +// import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; + +// contract ERC6551OpenfortAccountTest is OpenfortBaseTest { +// using ECDSA for bytes32; + +// ERC6551Registry public erc6551Registry; +// ERC6551OpenfortAccount public erc6551OpenfortAccount; +// ERC6551OpenfortAccount public erc6551OpenfortAccountImpl; +// ERC6551OpenfortProxy public erc6551OpenfortAccountProxy; +// MockERC721 public mockERC721; + +// /** +// * @notice Initialize the StaticOpenfortAccount testing contract. +// * Scenario: +// * - factoryAdmin is the deployer (and owner) of the mockNFT +// * - accountAdmin is the account used to deploy new static accounts +// * - entryPoint is the singleton EntryPoint +// * - testCounter is the counter used to test userOps +// */ +// function setUp() public { +// versionSalt = bytes32(0x0); +// // Setup and fund signers +// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); +// vm.deal(factoryAdmin, 100 ether); +// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); +// vm.deal(accountAdmin, 100 ether); + +// uint256 chainId; +// assembly { +// chainId := chainid() +// } +// console.log("ChainId:", chainId); + +// vm.startPrank(factoryAdmin); + +// // If we are in a fork +// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { +// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); +// } +// // If not a fork, deploy entryPoint (at the correct address) +// else { +// EntryPoint entryPoint_aux = new EntryPoint(); +// bytes memory code = address(entryPoint_aux).code; +// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); +// vm.etch(targetAddr, code); +// entryPoint = EntryPoint(payable(targetAddr)); +// } + +// // If we are in a fork +// if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { +// erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); +// console.log("Using ERC6551 registry from a fork"); +// } +// // If not a fork, deploy ERC6551 registry (at the correct address) +// else { +// ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); +// bytes memory code = address(ERC6551Registry_aux).code; +// address targetAddr = address(vm.envAddress("ERC6551_REGISTRY_ADDRESS")); +// vm.etch(targetAddr, code); +// erc6551Registry = ERC6551Registry(payable(targetAddr)); +// } + +// // deploy a new MockERC721 collection +// mockERC721 = new MockERC721{salt: versionSalt}(); + +// erc6551OpenfortAccountImpl = new ERC6551OpenfortAccount{salt: versionSalt}(); + +// erc6551OpenfortAccountProxy = +// new ERC6551OpenfortProxy{salt: versionSalt}(address(erc6551OpenfortAccountImpl), ""); + +// erc6551OpenfortAccountProxy.implementation(); +// erc6551OpenfortAccountProxy.implementation(); +// erc6551OpenfortAccountProxy.implementation(); +// erc6551OpenfortAccountProxy.implementation(); + +// accountAddress = erc6551Registry.createAccount( +// address(erc6551OpenfortAccountProxy), versionSalt, chainId, address(mockERC721), 1 +// ); + +// vm.stopPrank(); + +// erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(accountAddress)); +// vm.expectRevert(); +// erc6551OpenfortAccount.initialize(address(entryPoint)); +// console.log("State: ", erc6551OpenfortAccount.state()); + +// vm.expectRevert(OpenfortErrorsAndEvents.NotOwner.selector); +// erc6551OpenfortAccount.initialize(address(entryPoint)); + +// vm.prank(factoryAdmin); +// mockERC721.mint(accountAdmin, 1); + +// vm.prank(accountAdmin); +// erc6551OpenfortAccount.initialize(address(entryPoint)); +// } + +// /* +// * Test get implementation from proxy. +// */ +// function testGetImplementation() public view { +// ERC6551OpenfortProxy p = ERC6551OpenfortProxy(payable(accountAddress)); +// p.implementation(); +// } + +// /* +// * Test reinitialize. It should fail. +// */ +// function testFailReinitialize() public { +// vm.prank(accountAdmin); +// erc6551OpenfortAccount.initialize(address(entryPoint)); +// } + +// /* +// * Test initialize implementation. It should fail. +// */ +// function testFailInitializeImplementation() public { +// erc6551OpenfortAccountImpl.initialize(address(entryPoint)); +// } + +// /* +// * Test deploy. Regular, no userOps. +// */ +// function testERC6551Deploy() public { +// address deployedAccount = +// erc6551Registry.createAccount(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); + +// assertTrue(deployedAccount != address(0)); + +// address predictedAccount = +// erc6551Registry.account(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); + +// assertEq(predictedAccount, deployedAccount); +// } + +// /* +// * Check implementation has not been initialized. +// * EntryPoint address should be 0. Should pass. +// */ +// function testImplementationNoEntryPointAddr() public { +// IEntryPoint e = erc6551OpenfortAccountImpl.entryPoint(); +// assertEq(address(e), address(0)); +// } + +// /* +// * Create a 2nd account using the same technique than in setup with a new salt (2). +// */ +// function testCreate2ndAcc() public { +// uint256 chainId; +// assembly { +// chainId := chainid() +// } +// address accountAddress2 = erc6551Registry.createAccount( +// address(erc6551OpenfortAccountImpl), bytes32(0), chainId, address(mockERC721), 1 +// ); + +// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); +// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); +// assertEq(address(e), address(entryPoint)); +// assertNotEq(address(e), accountAddress2); +// } + +// /* +// * Create a new account using createAccount() and the initializer. +// */ +// function testCreateAccInitializer() public { +// uint256 chainId; +// assembly { +// chainId := chainid() +// } +// address accountAddress2 = erc6551Registry.createAccount( +// address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 +// ); +// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); +// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); +// assertEq(address(e), address(entryPoint)); +// } + +// /* +// * Create a new account using createAccount() and the initializer. +// * Test initialize again should fail. +// */ +// function testFailCreateAccInitializerNoReinit() public { +// uint256 chainId; +// assembly { +// chainId := chainid() +// } +// address accountAddress2 = erc6551Registry.createAccount( +// address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 +// ); + +// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); +// erc6551OpenfortAccount2.initialize(address(entryPoint)); +// } + +// /* +// * Test getDeposit() function. +// * First ERC4337 function called by this ERC6551-compatible account. +// */ +// function testGetDeposit() public { +// uint256 deposit; +// deposit = erc6551OpenfortAccount.getDeposit(); +// assertEq(deposit, 0); + +// // We can add deposit by directly calling the EntryPoint +// entryPoint.depositTo{value: 1 ether}(address(erc6551OpenfortAccount)); +// deposit = erc6551OpenfortAccount.getDeposit(); +// assertEq(deposit, 1 ether); + +// // We can ALSO add deposit by calling the EntryPoint depositTo() function +// vm.prank(accountAdmin); +// erc6551OpenfortAccount.execute{value: 1 ether}( +// address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) +// ); +// deposit = erc6551OpenfortAccount.getDeposit(); +// assertEq(deposit, 2 ether); +// } + +// /* +// * Test owner() function. +// * Check that the owner of the erc6551 account is the owner of the NFT +// */ +// function testOwner() public { +// assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); +// assertEq(erc6551OpenfortAccount.owner(), accountAdmin); +// } + +// /* +// * Test owner() function. +// * Check that the owner of the erc6551 account is the owner of the NFT +// */ +// function testNotOwner() public { +// // Burning the NFT +// vm.prank(accountAdmin); +// mockERC721.transferFrom(accountAdmin, address(1), 1); + +// assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); +// assertNotEq(erc6551OpenfortAccount.owner(), accountAdmin); +// assertEq(erc6551OpenfortAccount.owner(), address(1)); +// } + +// /* +// * Create an account by directly calling the registry. +// */ +// function testCreateAccountWithNonceViaRegistry() public { +// uint256 chainId; +// assembly { +// chainId := chainid() +// } + +// // Get the counterfactual address +// vm.prank(factoryAdmin); +// address accountAddress2 = +// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); + +// // Expect that we will see an event containing the account and admin +// // vm.expectEmit(true, true, false, true); +// // emit IERC6551Registry.ERC6551AccountCreated( +// // accountAddress2, address(erc6551OpenfortAccount), chainId, address(mockERC721), 1, 2 +// // ); + +// // Deploy a static account to the counterfactual address +// vm.prank(factoryAdmin); +// erc6551Registry.createAccount(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); + +// // Make sure the counterfactual address has not been altered +// vm.prank(factoryAdmin); +// assertEq( +// accountAddress2, +// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) +// ); +// // assertNotEq( +// // accountAddress2, +// // erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) +// // ); +// assertNotEq( +// accountAddress2, +// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) +// ); +// assertNotEq( +// accountAddress2, +// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(0), 1) +// ); +// } +// } From 7ac0f4fcb104990ade9161feca82625d127aabb5 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 24 Nov 2023 14:38:31 +0100 Subject: [PATCH 45/83] Adding ReentrancyGuard --- contracts/core/base/BaseOpenfortAccount.sol | 7 +++++-- contracts/core/base/BaseRecoverableAccount.sol | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 83703d2..96a4569 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -4,8 +4,9 @@ pragma solidity =0.8.19; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; -import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; +import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; +import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; import {BaseAccount, UserOperation, IEntryPoint, UserOperationLib} from "account-abstraction/core/BaseAccount.sol"; import {_packValidationData} from "account-abstraction/core/Helpers.sol"; import {TokenCallbackHandler} from "./TokenCallbackHandler.sol"; @@ -24,6 +25,7 @@ import {OpenfortErrorsAndEvents} from "../../interfaces/OpenfortErrorsAndEvents. abstract contract BaseOpenfortAccount is BaseAccount, Initializable, + ReentrancyGuardUpgradeable, EIP712Upgradeable, IERC1271Upgradeable, TokenCallbackHandler, @@ -176,7 +178,7 @@ abstract contract BaseOpenfortAccount is /** * Execute a transaction (called directly from owner, or by entryPoint) */ - function execute(address dest, uint256 value, bytes calldata func) public payable virtual { + function execute(address dest, uint256 value, bytes calldata func) public payable virtual nonReentrant { _requireFromEntryPointOrOwner(); _call(dest, value, func); } @@ -188,6 +190,7 @@ abstract contract BaseOpenfortAccount is public payable virtual + nonReentrant { _requireFromEntryPointOrOwner(); if (_target.length > 9 || _target.length != _calldata.length || _target.length != _value.length) { diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 5ea84fe..59ceb64 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -106,6 +106,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg _transferOwnership(_defaultAdmin); entrypointContract = _entrypoint; __EIP712_init("Openfort", "0.5"); + __ReentrancyGuard_init(); recoveryPeriod = _recoveryPeriod; lockPeriod = _lockPeriod; From 15793d4612ece31a727468015a3584a9b1fb38a9 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 24 Nov 2023 17:42:31 +0100 Subject: [PATCH 46/83] Moar tests --- contracts/core/base/BaseOpenfortAccount.sol | 5 +- .../core/base/BaseRecoverableAccount.sol | 1 - test/foundry/core/OpenfortBaseTest.t.sol | 4 + .../managed/ManagedOpenfortAccountTest.t.sol | 6 +- .../UpgradeableOpenfortAccountTest.t.sol | 263 +++++++++++++++++- 5 files changed, 264 insertions(+), 15 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 96a4569..2b1f7f1 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -5,7 +5,6 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini import {ECDSAUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; -import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; import {BaseAccount, UserOperation, IEntryPoint, UserOperationLib} from "account-abstraction/core/BaseAccount.sol"; import {_packValidationData} from "account-abstraction/core/Helpers.sol"; @@ -25,7 +24,6 @@ import {OpenfortErrorsAndEvents} from "../../interfaces/OpenfortErrorsAndEvents. abstract contract BaseOpenfortAccount is BaseAccount, Initializable, - ReentrancyGuardUpgradeable, EIP712Upgradeable, IERC1271Upgradeable, TokenCallbackHandler, @@ -178,7 +176,7 @@ abstract contract BaseOpenfortAccount is /** * Execute a transaction (called directly from owner, or by entryPoint) */ - function execute(address dest, uint256 value, bytes calldata func) public payable virtual nonReentrant { + function execute(address dest, uint256 value, bytes calldata func) public payable virtual { _requireFromEntryPointOrOwner(); _call(dest, value, func); } @@ -190,7 +188,6 @@ abstract contract BaseOpenfortAccount is public payable virtual - nonReentrant { _requireFromEntryPointOrOwner(); if (_target.length > 9 || _target.length != _calldata.length || _target.length != _value.length) { diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 59ceb64..5ea84fe 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -106,7 +106,6 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg _transferOwnership(_defaultAdmin); entrypointContract = _entrypoint; __EIP712_init("Openfort", "0.5"); - __ReentrancyGuard_init(); recoveryPeriod = _recoveryPeriod; lockPeriod = _lockPeriod; diff --git a/test/foundry/core/OpenfortBaseTest.t.sol b/test/foundry/core/OpenfortBaseTest.t.sol index d9ba918..fb01741 100644 --- a/test/foundry/core/OpenfortBaseTest.t.sol +++ b/test/foundry/core/OpenfortBaseTest.t.sol @@ -7,6 +7,8 @@ import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC20} from "contracts/mock/MockERC20.sol"; +import {MockERC721} from "contracts/mock/MockERC721.sol"; +import {MockERC1155} from "contracts/mock/MockERC1155.sol"; import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; contract OpenfortBaseTest is Test { @@ -19,6 +21,8 @@ contract OpenfortBaseTest is Test { address public accountAddress; TestCounter public testCounter; MockERC20 public mockERC20; + MockERC721 public mockERC721; + MockERC1155 public mockERC1155; // Testing addresses address public factoryAdmin; diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index e399455..bc8fe86 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -6,13 +6,12 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {MockERC20} from "contracts/mock/MockERC20.sol"; import {ManagedOpenfortAccount} from "contracts/core/managed/ManagedOpenfortAccount.sol"; import {ManagedOpenfortFactory} from "contracts/core/managed/ManagedOpenfortFactory.sol"; import {ManagedOpenfortProxy} from "contracts/core/managed/ManagedOpenfortProxy.sol"; import {MockV2ManagedOpenfortAccount} from "contracts/mock/MockV2ManagedOpenfortAccount.sol"; import {IBaseOpenfortFactory} from "contracts/interfaces/IBaseOpenfortFactory.sol"; -import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; +import {OpenfortBaseTest, MockERC20, MockERC721, MockERC1155} from "../OpenfortBaseTest.t.sol"; contract ManagedOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; @@ -64,10 +63,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ); // Create an managed account wallet and get its address accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1"); + // deploy a new TestCounter testCounter = new TestCounter{salt: versionSalt}(); // deploy a new MockERC20 (ERC20) mockERC20 = new MockERC20{salt: versionSalt}(); + mockERC721 = new MockERC721{salt: versionSalt}(); + mockERC1155 = new MockERC1155{salt: versionSalt}(); vm.stopPrank(); } diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 2f4cff3..ac7449b 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -6,7 +6,6 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {MockERC20} from "contracts/mock/MockERC20.sol"; import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; import {IUpgradeableOpenfortAccount} from "contracts/interfaces/IUpgradeableOpenfortAccount.sol"; import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; @@ -14,7 +13,8 @@ import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/Upgradeable import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {UpgradeableOpenfortProxy} from "contracts/core/upgradeable/UpgradeableOpenfortProxy.sol"; import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; -import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; +import {OpenfortBaseTest, MockERC20, MockERC721, MockERC1155} from "../OpenfortBaseTest.t.sol"; +import {SimpleNFT} from "contracts/mock/SimpleNFT.sol"; contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; @@ -72,9 +72,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1"); // deploy a new TestCounter - testCounter = new TestCounter(); + testCounter = new TestCounter{salt: versionSalt}(); // deploy a new MockERC20 (ERC20) - mockERC20 = new MockERC20(); + mockERC20 = new MockERC20{salt: versionSalt}(); + mockERC721 = new MockERC721{salt: versionSalt}(); + mockERC1155 = new MockERC1155{salt: versionSalt}(); vm.stopPrank(); } @@ -1018,6 +1020,69 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(mockERC20.totalSupply(), 2); } + /* + * Test an account for reentrancy call execute that calls count. + */ + function testReentrancy() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + // Make the admin of the account wallet call "count" + vm.prank(accountAdmin); + IBaseRecoverableAccount(payable(accountAddress)).execute( + address(testCounter), 0, abi.encodeWithSignature("count()") + ); + + // Verify that the counter has increased + assertEq(testCounter.counters(accountAddress), 1); + + // Make the admin of the account wallet call "execute" which call count + vm.expectRevert(OpenfortErrorsAndEvents.NotOwnerOrEntrypoint.selector); + vm.prank(accountAdmin); + IBaseRecoverableAccount(payable(accountAddress)).execute( + address(accountAddress), + 0, + abi.encodeWithSignature( + "execute(address,uint256,bytes)", testCounter, 0, abi.encodeWithSignature("count()") + ) + ); + } + + /* + * Test an account for reentrancy call executeBatch that calls count. + */ + function testReentrancyBatch() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + // Make the admin of the account wallet call "count" + vm.prank(accountAdmin); + IBaseRecoverableAccount(payable(accountAddress)).execute( + address(testCounter), 0, abi.encodeWithSignature("count()") + ); + + // Verify that the counter has increased + assertEq(testCounter.counters(accountAddress), 1); + + uint256 count = 1; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + for (uint256 i = 0; i < count; i += 1) { + targets[i] = address(accountAddress); + values[i] = 0; + callData[i] = abi.encodeWithSignature( + "execute(address,uint256,bytes)", testCounter, 0, abi.encodeWithSignature("count()") + ); + } + + // Make the admin of the account wallet call "execute" which call count + vm.expectRevert(OpenfortErrorsAndEvents.NotOwnerOrEntrypoint.selector); + vm.prank(accountAdmin); + IBaseRecoverableAccount(payable(accountAddress)).executeBatch(targets, values, callData); + } + /* * Test receive native tokens. */ @@ -1146,7 +1211,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(address(upgradeableAccount.entryPoint()), newEntryPoint); } - function testFailIsValidSignature() public { + function testIsValidSignature() public { bytes32 hash = keccak256("Signed by Owner"); (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hash); address signer = ecrecover(hash, v, r, s); @@ -1158,10 +1223,10 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! - assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore + assertNotEq(valid, MAGICVALUE); // We do not accept straight signatures from owners anymore } - function testFailIsValidSignatureMessage() public { + function testIsValidSignatureMessage() public { bytes32 hash = keccak256("Signed by Owner"); bytes32 hashMessage = hash.toEthSignedMessageHash(); (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hashMessage); @@ -1174,7 +1239,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! - assertEq(valid, MAGICVALUE); // SHOULD FAIL! We do not accept straight signatures from owners anymore + assertNotEq(valid, MAGICVALUE); // We do not accept straight signatures from owners anymore } /* @@ -1216,6 +1281,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(accountAdmin, signer); // [PASS] bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); + assertNotEq(valid, bytes4(0xffffffff)); // SHOULD PASS assertEq(valid, MAGICVALUE); // SHOULD PASS } @@ -2543,4 +2609,185 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // New owner should be now newOwner assertEq(openfortAccount.owner(), address(newOwner)); } + + /** + * Transfer tokens between OF accounts tests * + */ + + /* + * Test an account with mockERC20 sending it to another. + */ + function testTransferERC20BetweenAccounts() public { + // Verify that the totalSupply is still 0 + assertEq(mockERC20.totalSupply(), 0); + + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2"); + + UserOperation[] memory userOp = _setupUserOpExecute( + accountAddress, + accountAdminPKey, + bytes(""), + address(mockERC20), + 0, + abi.encodeWithSignature("mint(address,uint256)", accountAddress, 1) + ); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + assertEq(mockERC20.balanceOf(accountAddress), 1); + assertEq(mockERC20.balanceOf(accountAddress2), 0); + + mockERC20.mint(accountAddress, 1); + + assertEq(mockERC20.balanceOf(accountAddress), 2); + assertEq(mockERC20.balanceOf(accountAddress2), 0); + + userOp = _setupUserOpExecute( + accountAddress, + accountAdminPKey, + bytes(""), + address(mockERC20), + 0, + abi.encodeWithSignature("transfer(address,uint256)", accountAddress2, 1) + ); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + assertEq(mockERC20.balanceOf(accountAddress), 1); + assertEq(mockERC20.balanceOf(accountAddress2), 1); + + // Verify that the totalSupply has increased + assertEq(mockERC20.totalSupply(), 2); + } + + /* + * Test an account with mockERC721 sending it to another and back. + */ + function testTransferERC721BetweenAccounts() public { + assertEq(mockERC721.balanceOf(accountAddress), 0); + + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2"); + + UserOperation[] memory userOp = _setupUserOpExecute( + accountAddress, + accountAdminPKey, + bytes(""), + address(mockERC721), + 0, + abi.encodeWithSignature("mint(address,uint256)", accountAddress, 1) + ); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + assertEq(mockERC721.balanceOf(accountAddress), 1); + assertEq(mockERC721.balanceOf(accountAddress2), 0); + + mockERC721.mint(accountAddress, 2); + + assertEq(mockERC721.balanceOf(accountAddress), 2); + assertEq(mockERC721.balanceOf(accountAddress2), 0); + + userOp = _setupUserOpExecute( + accountAddress, + accountAdminPKey, + bytes(""), + address(mockERC721), + 0, + abi.encodeWithSignature("transferFrom(address,address,uint256)", accountAddress, accountAddress2, 1) + ); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + assertEq(mockERC721.balanceOf(accountAddress), 1); + assertEq(mockERC721.balanceOf(accountAddress2), 1); + + userOp = _setupUserOpExecute( + accountAddress2, + accountAdminPKey, + bytes(""), + address(mockERC721), + 0, + abi.encodeWithSignature("safeTransferFrom(address,address,uint256)", accountAddress2, accountAddress, 1) + ); + + entryPoint.depositTo{value: 1 ether}(accountAddress2); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + assertEq(mockERC721.balanceOf(accountAddress), 2); + assertEq(mockERC721.balanceOf(accountAddress2), 0); + } + + /* + * Test an account with mockERC1155 sending it to another. + */ + function testTransferERC1155BetweenAccounts() public { + assertEq(mockERC1155.balanceOf(accountAddress, 1), 0); + + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2"); + + UserOperation[] memory userOp = _setupUserOpExecute( + accountAddress, + accountAdminPKey, + bytes(""), + address(mockERC1155), + 0, + abi.encodeWithSignature("mint(address,uint256,uint256)", accountAddress, 1, 1) + ); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + assertEq(mockERC1155.balanceOf(accountAddress, 1), 1); + assertEq(mockERC1155.balanceOf(accountAddress2, 1), 0); + + mockERC1155.mint(accountAddress, 1, 1); + + assertEq(mockERC1155.balanceOf(accountAddress, 1), 2); + assertEq(mockERC1155.balanceOf(accountAddress2, 1), 0); + + userOp = _setupUserOpExecute( + accountAddress, + accountAdminPKey, + bytes(""), + address(mockERC1155), + 0, + abi.encodeWithSignature( + "safeTransferFrom(address,address,uint256,uint256,bytes)", accountAddress, accountAddress2, 1, 1, "" + ) + ); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + assertEq(mockERC1155.balanceOf(accountAddress, 1), 1); + assertEq(mockERC1155.balanceOf(accountAddress2, 1), 1); + } + + /* + * Test for coverage purposes. + * SimpleNFT is an NFT contract used by Openfort in some internal tests + */ + function testSimpleNFT() public { + SimpleNFT simpleNFT = new SimpleNFT(); + simpleNFT.mint(accountAddress); + assertEq(simpleNFT.balanceOf(accountAddress), 1); + } } From acc60485637be37c41711554469b9312150ad95d Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 24 Nov 2023 18:57:09 +0100 Subject: [PATCH 47/83] Moar test coverage --- contracts/core/base/TokenCallbackHandler.sol | 4 ++-- .../core/managed/ManagedOpenfortAccountTest.t.sol | 14 ++++++++++++++ .../UpgradeableOpenfortAccountTest.t.sol | 13 +++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/contracts/core/base/TokenCallbackHandler.sol b/contracts/core/base/TokenCallbackHandler.sol index 34a27b0..15e0f58 100644 --- a/contracts/core/base/TokenCallbackHandler.sol +++ b/contracts/core/base/TokenCallbackHandler.sol @@ -46,7 +46,7 @@ contract TokenCallbackHandler is IERC777Recipient, IERC721Receiver, IERC1155Rece } function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) { - return interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IERC1155Receiver).interfaceId - || interfaceId == type(IERC165).interfaceId; + return interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IERC777Recipient).interfaceId + || interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC165).interfaceId; } } diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index bc8fe86..40aeecf 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -6,12 +6,17 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; +import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; import {ManagedOpenfortAccount} from "contracts/core/managed/ManagedOpenfortAccount.sol"; import {ManagedOpenfortFactory} from "contracts/core/managed/ManagedOpenfortFactory.sol"; import {ManagedOpenfortProxy} from "contracts/core/managed/ManagedOpenfortProxy.sol"; import {MockV2ManagedOpenfortAccount} from "contracts/mock/MockV2ManagedOpenfortAccount.sol"; import {IBaseOpenfortFactory} from "contracts/interfaces/IBaseOpenfortFactory.sol"; import {OpenfortBaseTest, MockERC20, MockERC721, MockERC1155} from "../OpenfortBaseTest.t.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; contract ManagedOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; @@ -2453,4 +2458,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // New owner should be now newOwner assertEq(openfortAccount.owner(), address(newOwner)); } + + function testSupportsInterface() public { + IBaseRecoverableAccount account = IBaseRecoverableAccount(payable(accountAddress)); + assertTrue(account.supportsInterface(type(IERC721Receiver).interfaceId)); + assertTrue(account.supportsInterface(type(IERC777Recipient).interfaceId)); + assertTrue(account.supportsInterface(type(IERC1155Receiver).interfaceId)); + assertTrue(account.supportsInterface(type(IERC165).interfaceId)); + assertFalse(account.supportsInterface(bytes4(0x0000))); + } } diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index ac7449b..df96d4f 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -15,6 +15,10 @@ import {UpgradeableOpenfortProxy} from "contracts/core/upgradeable/UpgradeableOp import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; import {OpenfortBaseTest, MockERC20, MockERC721, MockERC1155} from "../OpenfortBaseTest.t.sol"; import {SimpleNFT} from "contracts/mock/SimpleNFT.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; @@ -2790,4 +2794,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { simpleNFT.mint(accountAddress); assertEq(simpleNFT.balanceOf(accountAddress), 1); } + + function testSupportsInterface() public { + IBaseRecoverableAccount account = IBaseRecoverableAccount(payable(accountAddress)); + assertTrue(account.supportsInterface(type(IERC721Receiver).interfaceId)); + assertTrue(account.supportsInterface(type(IERC777Recipient).interfaceId)); + assertTrue(account.supportsInterface(type(IERC1155Receiver).interfaceId)); + assertTrue(account.supportsInterface(type(IERC165).interfaceId)); + assertFalse(account.supportsInterface(bytes4(0x0000))); + } } From 766c2f6abaf4ef8852a7674e98e056a1fb18329e Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 27 Nov 2023 10:25:06 +0100 Subject: [PATCH 48/83] Test keys from old owner should not work --- .../managed/ManagedOpenfortAccountTest.t.sol | 51 +++++++++++++++++++ .../UpgradeableOpenfortAccountTest.t.sol | 51 +++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 40aeecf..cfbafec 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -2459,6 +2459,57 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(openfortAccount.owner(), address(newOwner)); } + /* + * Try to use a sessionKey that is registered by the previous owner. + * Should not work. + */ + function testOldSessionKey() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + address sessionKey; + uint256 sessionKeyPrivKey; + (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); + + // Original owner registers a session key + vm.prank(accountAdmin); + openfortAccount.registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); + + // Using the session key + UserOperation[] memory userOp = _setupUserOpExecute( + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + ); + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + // Verify that the counter has increased + assertEq(testCounter.counters(accountAddress), 1); + + // Register a new owner + address newOwner = makeAddr("newOwner"); + vm.prank(accountAdmin); + openfortAccount.transferOwnership(newOwner); + vm.prank(newOwner); + openfortAccount.acceptOwnership(); + + // Trying to use the session key registered by the old owner + userOp = _setupUserOpExecute( + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + ); + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + vm.expectRevert(); + entryPoint.handleOps(userOp, beneficiary); + + // Verify that the counter has not increased this time + assertEq(testCounter.counters(accountAddress), 1); + } + function testSupportsInterface() public { IBaseRecoverableAccount account = IBaseRecoverableAccount(payable(accountAddress)); assertTrue(account.supportsInterface(type(IERC721Receiver).interfaceId)); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index df96d4f..fd05b32 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -2614,6 +2614,57 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(openfortAccount.owner(), address(newOwner)); } + /* + * Try to use a sessionKey that is registered by the previous owner. + * Should not work. + */ + function testOldSessionKey() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + address sessionKey; + uint256 sessionKeyPrivKey; + (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); + + // Original owner registers a session key + vm.prank(accountAdmin); + openfortAccount.registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); + + // Using the session key + UserOperation[] memory userOp = _setupUserOpExecute( + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + ); + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + // Verify that the counter has increased + assertEq(testCounter.counters(accountAddress), 1); + + // Register a new owner + address newOwner = makeAddr("newOwner"); + vm.prank(accountAdmin); + openfortAccount.transferOwnership(newOwner); + vm.prank(newOwner); + openfortAccount.acceptOwnership(); + + // Trying to use the session key registered by the old owner + userOp = _setupUserOpExecute( + accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + ); + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + vm.expectRevert(); + entryPoint.handleOps(userOp, beneficiary); + + // Verify that the counter has not increased this time + assertEq(testCounter.counters(accountAddress), 1); + } + /** * Transfer tokens between OF accounts tests * */ From c12dbb5b3246f860797d7b84bc7d1763fe7c2329 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 27 Nov 2023 13:04:07 +0100 Subject: [PATCH 49/83] Towards accepting multiple initial guardians --- contracts/core/base/BaseOpenfortFactory.sol | 26 +---------- .../core/base/BaseRecoverableAccount.sol | 23 ++++++---- .../UpgradeableOpenfortFactory.sol | 43 ++++++++++++------- 3 files changed, 44 insertions(+), 48 deletions(-) diff --git a/contracts/core/base/BaseOpenfortFactory.sol b/contracts/core/base/BaseOpenfortFactory.sol index bcd3b15..f1a84f4 100644 --- a/contracts/core/base/BaseOpenfortFactory.sol +++ b/contracts/core/base/BaseOpenfortFactory.sol @@ -16,37 +16,15 @@ import {IBaseOpenfortFactory} from "../../interfaces/IBaseOpenfortFactory.sol"; abstract contract BaseOpenfortFactory is IBaseOpenfortFactory, Ownable2Step { address public entrypointContract; address internal _implementation; - uint256 public recoveryPeriod; - uint256 public securityPeriod; - uint256 public securityWindow; - uint256 public lockPeriod; - address public openfortGuardian; error InsecurePeriod(); - constructor( - address _owner, - address _entrypoint, - address _accountImplementation, - uint256 _recoveryPeriod, - uint256 _securityPeriod, - uint256 _securityWindow, - uint256 _lockPeriod, - address _openfortGuardian - ) { - if (_owner == address(0) || _openfortGuardian == address(0)) revert ZeroAddressNotAllowed(); + constructor(address _owner, address _entrypoint, address _accountImplementation) { + if (_owner == address(0)) revert ZeroAddressNotAllowed(); if (!Address.isContract(_entrypoint) || !Address.isContract(_accountImplementation)) revert NotAContract(); - if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { - revert InsecurePeriod(); - } _transferOwnership(_owner); entrypointContract = _entrypoint; _implementation = _accountImplementation; - recoveryPeriod = _recoveryPeriod; - securityPeriod = _securityPeriod; - securityWindow = _securityWindow; - lockPeriod = _lockPeriod; - openfortGuardian = _openfortGuardian; } /** diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 5ea84fe..8ab7d23 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -20,9 +20,10 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg address internal entrypointContract; + // Recoverable account settings (cannot be modified once created) // Period during which the owner can cancel a guardian proposal/revocation in seconds (7 days) uint256 internal recoveryPeriod; - // Default lock period (cannot be modified) + // Default lock period uint256 internal lockPeriod; // The security period to add/remove guardians uint256 internal securityPeriod; @@ -83,6 +84,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg error OngoingRecovery(); error InvalidRecoverySignatures(); error InvalidSignatureAmount(); + error TooManyInitialGuardians(); /* * @notice Initialize the smart contract account. @@ -94,9 +96,9 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg uint256 _securityPeriod, uint256 _securityWindow, uint256 _lockPeriod, - address _openfortGuardian + address[] _initialGuardians ) public initializer { - if (_defaultAdmin == address(0) || _entrypoint == address(0) || _openfortGuardian == address(0)) { + if (_defaultAdmin == address(0) || _entrypoint == address(0)) { revert ZeroAddressNotAllowed(); } if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { @@ -112,11 +114,16 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg securityWindow = _securityWindow; securityPeriod = _securityPeriod; - guardiansConfig.guardians.push(_openfortGuardian); - guardiansConfig.info[_openfortGuardian].exists = true; - guardiansConfig.info[_openfortGuardian].index = 0; - guardiansConfig.info[_openfortGuardian].pending = 0; - emit GuardianAdded(_openfortGuardian); + uint256 initialGuardiansNumber = _initialGuardians.length; + if (initialGuardiansNumber > 5) revert TooManyInitialGuardians(); + + for (uint256 i = 0; i < array.length; i++) { + guardiansConfig.guardians.push(_initialGuardians[i]); + guardiansConfig.info[_initialGuardians[i]].exists = true; + guardiansConfig.info[_initialGuardians[i]].index = 0; + guardiansConfig.info[_initialGuardians[i]].pending = 0; + emit GuardianAdded(_initialGuardians[i]); + } } function owner() public view virtual override(BaseOpenfortAccount, OwnableUpgradeable) returns (address) { diff --git a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol index c691649..3f057b3 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol @@ -13,6 +13,13 @@ import {BaseOpenfortFactory} from "../base/BaseOpenfortFactory.sol"; * - BaseOpenfortFactory */ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { + uint256 public recoveryPeriod; + uint256 public securityPeriod; + uint256 public securityWindow; + uint256 public lockPeriod; + + error TooManyInitialGuardians(); + constructor( address _owner, address _entrypoint, @@ -20,25 +27,24 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { uint256 _recoveryPeriod, uint256 _securityPeriod, uint256 _securityWindow, - uint256 _lockPeriod, - address _openfortGuardian - ) - BaseOpenfortFactory( - _owner, - _entrypoint, - _accountImplementation, - _recoveryPeriod, - _securityPeriod, - _securityWindow, - _lockPeriod, - _openfortGuardian - ) - {} + uint256 _lockPeriod + ) BaseOpenfortFactory(_owner, _entrypoint, _accountImplementation) { + if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { + revert InsecurePeriod(); + } + recoveryPeriod = _recoveryPeriod; + securityPeriod = _securityPeriod; + securityWindow = _securityWindow; + lockPeriod = _lockPeriod; + } /* * @notice Deploy a new account for _admin with a nonce. */ - function createAccountWithNonce(address _admin, bytes32 _nonce) external returns (address account) { + function createAccountWithNonce(address _admin, bytes32 _nonce, address[] _initialGuardians) + external + returns (address account) + { bytes32 salt = keccak256(abi.encode(_admin, _nonce)); account = getAddressWithNonce(_admin, _nonce); @@ -46,8 +52,13 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { emit AccountCreated(account, _admin); account = address(new UpgradeableOpenfortProxy{salt: salt}(_implementation, "")); + uint256 initialGuardiansNumber = _initialGuardians.length; + if (initialGuardiansNumber > 5) revert TooManyInitialGuardians(); + for (uint256 i = 0; i < initialGuardiansNumber; i++) { + if (_initialGuardians[i] == address(0)) revert ZeroAddressNotAllowed(); + } UpgradeableOpenfortAccount(payable(account)).initialize( - _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, openfortGuardian + _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, _initialGuardians ); } From 4b414e510ef214c7994cfe52ee6e23e4358a88d8 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 27 Nov 2023 17:15:11 +0100 Subject: [PATCH 50/83] updated forge --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index c22437a..2f11269 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit c22437a63d1c3418869bc35a7c55a5175a09b701 +Subproject commit 2f112697506eab12d433a65fdc31a639548fe365 From 29c1540b8ddd4b0ab0a75463788e3afa7501e7ef Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 28 Nov 2023 11:01:34 +0100 Subject: [PATCH 51/83] Initial guardians as array --- .../core/base/BaseRecoverableAccount.sol | 11 +- .../core/managed/ManagedOpenfortFactory.sol | 42 +++-- .../UpgradeableOpenfortFactory.sol | 2 +- contracts/interfaces/IBaseOpenfortFactory.sol | 4 +- hardhat.config.ts | 5 +- script/UserOpTestCounter.s.sol | 148 ------------------ script/deployManagedAccounts.s.sol | 90 +++++------ script/deployOpenfortPaymasterV2.s.sol | 3 +- .../managed/ManagedOpenfortAccountTest.t.sol | 28 ++-- .../UpgradeableOpenfortAccountTest.t.sol | 40 ++--- .../paymaster/OpenfortPaymasterV2Test.t.sol | 9 +- 11 files changed, 130 insertions(+), 252 deletions(-) delete mode 100644 script/UserOpTestCounter.s.sol diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 8ab7d23..e513516 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -96,7 +96,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg uint256 _securityPeriod, uint256 _securityWindow, uint256 _lockPeriod, - address[] _initialGuardians + address[] memory _initialGuardians ) public initializer { if (_defaultAdmin == address(0) || _entrypoint == address(0)) { revert ZeroAddressNotAllowed(); @@ -117,7 +117,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg uint256 initialGuardiansNumber = _initialGuardians.length; if (initialGuardiansNumber > 5) revert TooManyInitialGuardians(); - for (uint256 i = 0; i < array.length; i++) { + for (uint256 i = 0; i < initialGuardiansNumber; i++) { guardiansConfig.guardians.push(_initialGuardians[i]); guardiansConfig.info[_initialGuardians[i]].exists = true; guardiansConfig.info[_initialGuardians[i]].index = 0; @@ -420,4 +420,11 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg if (isGuardian(_newOwner)) revert GuardianCannotBeOwner(); super.transferOwnership(_newOwner); } + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[49] private __gap; } diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index 2d81bec..fc24da8 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -15,6 +15,13 @@ import {BaseOpenfortFactory, Address} from "../base/BaseOpenfortFactory.sol"; * - IBeacon to work as the beacon */ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { + uint256 public recoveryPeriod; + uint256 public securityPeriod; + uint256 public securityWindow; + uint256 public lockPeriod; + + error TooManyInitialGuardians(); + /** * @dev Emitted when the implementation returned by the beacon is changed. */ @@ -27,27 +34,25 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { uint256 _recoveryPeriod, uint256 _securityPeriod, uint256 _securityWindow, - uint256 _lockPeriod, - address _openfortGuardian - ) - BaseOpenfortFactory( - _owner, - _entrypoint, - _accountImplementation, - _recoveryPeriod, - _securityPeriod, - _securityWindow, - _lockPeriod, - _openfortGuardian - ) - { + uint256 _lockPeriod + ) BaseOpenfortFactory(_owner, _entrypoint, _accountImplementation) { + if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { + revert InsecurePeriod(); + } + recoveryPeriod = _recoveryPeriod; + securityPeriod = _securityPeriod; + securityWindow = _securityWindow; + lockPeriod = _lockPeriod; _setImplementation(_accountImplementation); } /* * @notice Deploy a new account for _admin with a nonce. */ - function createAccountWithNonce(address _admin, bytes32 _nonce) external returns (address account) { + function createAccountWithNonce(address _admin, bytes32 _nonce, address[] memory _initialGuardians) + external + returns (address account) + { bytes32 salt = keccak256(abi.encode(_admin, _nonce)); account = getAddressWithNonce(_admin, _nonce); @@ -56,8 +61,13 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { emit AccountCreated(account, _admin); account = address(new ManagedOpenfortProxy{salt: salt}(address(this), "")); + uint256 initialGuardiansNumber = _initialGuardians.length; + if (initialGuardiansNumber > 5) revert TooManyInitialGuardians(); + for (uint256 i = 0; i < initialGuardiansNumber; i++) { + if (_initialGuardians[i] == address(0)) revert ZeroAddressNotAllowed(); + } ManagedOpenfortAccount(payable(account)).initialize( - _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, openfortGuardian + _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, _initialGuardians ); } diff --git a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol index 3f057b3..87c981c 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol @@ -41,7 +41,7 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { /* * @notice Deploy a new account for _admin with a nonce. */ - function createAccountWithNonce(address _admin, bytes32 _nonce, address[] _initialGuardians) + function createAccountWithNonce(address _admin, bytes32 _nonce, address[] memory _initialGuardians) external returns (address account) { diff --git a/contracts/interfaces/IBaseOpenfortFactory.sol b/contracts/interfaces/IBaseOpenfortFactory.sol index b503d50..a9d6d42 100644 --- a/contracts/interfaces/IBaseOpenfortFactory.sol +++ b/contracts/interfaces/IBaseOpenfortFactory.sol @@ -12,7 +12,9 @@ interface IBaseOpenfortFactory { error NotAContract(); /// @notice Deploys a new Account for admin. - function createAccountWithNonce(address _admin, bytes32 _nonce) external returns (address account); + function createAccountWithNonce(address _admin, bytes32 _nonce, address[] memory _initialGuardians) + external + returns (address account); /// @notice Returns the address of the Account implementation. function implementation() external view returns (address); diff --git a/hardhat.config.ts b/hardhat.config.ts index 19e776b..1c958bb 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -19,7 +19,10 @@ const config: HardhatUserConfig = { solidity: { version: "0.8.19", settings: { - optimizer: { enabled: true }, + optimizer: { + enabled: true, + runs: 1000000, + }, }, }, networks: { diff --git a/script/UserOpTestCounter.s.sol b/script/UserOpTestCounter.s.sol deleted file mode 100644 index e08c7c2..0000000 --- a/script/UserOpTestCounter.s.sol +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -import {Script, console} from "forge-std/Script.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import { - UpgradeableOpenfortFactory, - UpgradeableOpenfortAccount -} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; -import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {EntryPoint} from "account-abstraction/core/EntryPoint.sol"; -import {UserOperation, UserOperationLib} from "account-abstraction/interfaces/UserOperation.sol"; - -contract UserOpTestCounter is Script { - using ECDSA for bytes32; - using UserOperationLib for UserOperation; - - uint256 public mumbaiFork = vm.createFork(vm.envString("POLYGON_MUMBAI_RPC")); - - uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); - address internal deployAddress = vm.addr(deployPrivKey); - EntryPoint internal entryPoint = EntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - - UpgradeableOpenfortFactory staticOpenfortFactory; - UpgradeableOpenfortAccount staticOpenfortAccount; - TestCounter testCounter; - - // WIP based on calcPreVerificationGas.ts from the bundler's SDK - function calcPreVerificationGas(UserOperation calldata userOp) public pure returns (uint256) { - uint256 fixedCost = 21_000; - uint256 perUserOp = 18_300; - uint256 lengthInWord = (userOp.pack().length + 31) / 32; - uint256 perUserOpWord = 4; - return fixedCost + perUserOp + lengthInWord * perUserOpWord; - } - - /* - * Auxiliary function to generate a userOP - */ - function _setupUserOp( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal view returns (UserOperation[] memory ops) { - uint256 nonce = entryPoint.getNonce(sender, 0); - - // Get user op fields - UserOperation memory op = UserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 80_000, - maxFeePerGas: 1_500_000_030, - maxPriorityFeePerGas: 1_500_000_000, - paymasterAndData: bytes(""), - signature: bytes("") - }); - - this.calcPreVerificationGas(op); - - // Sign UserOp - bytes32 opHash = entryPoint.getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - require(recoveredSigner == expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new UserOperation[](1); - ops[0] = op; - } - - /* - * Auxiliary function to generate a userOP using the execute() - * from the account - */ - function _setupUserOpExecute( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal view returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - /* - * Auxiliary function to generate a userOP using the executeBatch() - * from the account - */ - function _setupUserOpExecuteBatch( - address sender, - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal view returns (UserOperation[] memory) { - bytes memory callDataForEntrypoint = - abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - - return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); - } - - function setUp() public { - vm.selectFork(mumbaiFork); - - // Due to errors with Foundry and create2, let's use hardcoded addresses for testing: - staticOpenfortFactory = UpgradeableOpenfortFactory(0xe9B5fb44f377Ce5a03427d5Be7D9d073bf8FE1f0); - testCounter = new TestCounter(); - } - - function run() public { - vm.startBroadcast(deployPrivKey); - - // Verify that the counter is still set to 0 - assert(testCounter.counters(deployAddress) == 0); - // Count using deployPrivKey - testCounter.count(); - assert(testCounter.counters(deployAddress) == 1); - - address account = staticOpenfortFactory.createAccountWithNonce(deployAddress, "1"); - - // Count using userOp - UserOperation[] memory userOp = _setupUserOpExecute( - account, deployPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") - ); - - entryPoint.depositTo{value: 10000000000000000}(account); - entryPoint.handleOps(userOp, payable(deployAddress)); - - vm.stopBroadcast(); - } -} diff --git a/script/deployManagedAccounts.s.sol b/script/deployManagedAccounts.s.sol index a3e1ce8..3c55b9b 100644 --- a/script/deployManagedAccounts.s.sol +++ b/script/deployManagedAccounts.s.sol @@ -1,53 +1,49 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Script} from "forge-std/Script.sol"; +import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; import {ManagedOpenfortAccount} from "../contracts/core/managed/ManagedOpenfortAccount.sol"; import {ManagedOpenfortFactory} from "../contracts/core/managed/ManagedOpenfortFactory.sol"; -// import {MockedV2ManagedOpenfortAccount} from "../contracts/mock/MockedV2ManagedOpenfortAccount.sol"; - -// contract ManagedOpenfortDeploy is Script { -// uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); -// address internal deployAddress = vm.addr(deployPrivKey); -// IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - -// function run() public { -// bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); -// vm.startBroadcast(deployPrivKey); - -// // Create an acccount to server as implementation -// ManagedOpenfortAccount managedOpenfortAccount = new ManagedOpenfortAccount{salt: versionSalt}(); - -// // OpenfortBeacon openfortBeacon = new OpenfortBeacon(address(managedOpenfortAccount)); // not needed anymore - -// // Create a factory to deploy cloned accounts -// ManagedOpenfortFactory managedOpenfortFactory = -// new ManagedOpenfortFactory{salt: versionSalt}(deployAddress, address(entryPoint), address(managedOpenfortAccount)); -// (managedOpenfortFactory); -// // address account1 = managedOpenfortFactory.accountImplementation(); - -// // The first call should create a new account, while the second will just return the corresponding account address -// // address account2 = managedOpenfortFactory.createAccountWithNonce(deployAddress, "1"); -// // console.log( -// // "Factory at address %s has created an account at address %s", address(managedOpenfortFactory), account2 -// // ); - -// // MockedV2ManagedOpenfortAccount mockedOpenfortAccount = new MockedV2ManagedOpenfortAccount{salt: versionSalt}(); -// // (mockedOpenfortAccount); - -// // assert(account1 != account2); -// // address account3 = managedOpenfortFactory.createAccountWithNonce(deployAddress, 3); -// // console.log( -// // "Factory at address %s has created an account at address %s", address(managedOpenfortFactory), account3 -// // ); -// // assert(account2 != account3); -// // address account4 = managedOpenfortFactory.createAccountWithNonce(deployAddress, 4); -// // console.log( -// // "Factory at address %s has created an account at address %s", address(managedOpenfortFactory), account4 -// // ); -// // assert(account3 != account4); - -// vm.stopBroadcast(); -// } -// } + +contract ManagedOpenfortDeploy is Script { + uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_MAINNET"); + address internal deployAddress = vm.addr(deployPrivKey); + IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); + + uint256 private constant RECOVERY_PERIOD = 2 days; + uint256 private constant SECURITY_PERIOD = 1.5 days; + uint256 private constant SECURITY_WINDOW = 0.5 days; + uint256 private constant LOCK_PERIOD = 5 days; + address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_MAINNET"); + address[] initialGuardians; + + function run() public { + bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); + vm.startBroadcast(deployPrivKey); + initialGuardians = [OPENFORT_GUARDIAN]; + + // Create an acccount to serve as implementation + ManagedOpenfortAccount managedOpenfortAccountImpl = new ManagedOpenfortAccount{salt: versionSalt}(); + // deploy account factory (beacon) + ManagedOpenfortFactory openfortFactory = new ManagedOpenfortFactory{salt: versionSalt}( + deployAddress, + address(entryPoint), + address(managedOpenfortAccountImpl), + RECOVERY_PERIOD, + SECURITY_PERIOD, + SECURITY_WINDOW, + LOCK_PERIOD + ); + + address accountImpl = openfortFactory.implementation(); + console.log("Account implementation: ", accountImpl); + + // Create an managed account wallet and get its address + address firstAccountAddress = openfortFactory.createAccountWithNonce(deployAddress, "1", initialGuardians); + console.log(firstAccountAddress); + console.log("First Account Address: ", firstAccountAddress); + + vm.stopBroadcast(); + } +} diff --git a/script/deployOpenfortPaymasterV2.s.sol b/script/deployOpenfortPaymasterV2.s.sol index e30389e..27e711b 100644 --- a/script/deployOpenfortPaymasterV2.s.sol +++ b/script/deployOpenfortPaymasterV2.s.sol @@ -6,8 +6,7 @@ import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPo import {OpenfortPaymasterV2} from "../contracts/paymaster/OpenfortPaymasterV2.sol"; contract OpenfortPaymasterV2Deploy is Script { - uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC_PAYMASTER_OWNER_TESTNET"), 0); - // uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_MAINNET"); + uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_MAINNET"); address internal deployAddress = vm.addr(deployPrivKey); IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); uint32 internal constant UNSTAKEDELAYSEC = 8600; diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index cfbafec..e3b7f75 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -24,6 +24,8 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ManagedOpenfortAccount public managedOpenfortAccountImpl; ManagedOpenfortFactory public openfortFactory; + address[] initialGuardians; + /** * @notice Initialize the ManagedOpenfortAccount testing contract. * Scenario: @@ -40,6 +42,8 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.deal(accountAdmin, 100 ether); (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); + initialGuardians = [OPENFORT_GUARDIAN]; + vm.startPrank(factoryAdmin); // If we are in a fork if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { @@ -63,11 +67,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN + LOCK_PERIOD ); - // Create an managed account wallet and get its address - accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1"); + // Create a managed account wallet and get its address + accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1", initialGuardians); // deploy a new TestCounter testCounter = new TestCounter{salt: versionSalt}(); @@ -124,7 +127,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, - OPENFORT_GUARDIAN + initialGuardians ); } @@ -142,11 +145,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Deploy a managed account to the counterfactual address vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2"); + openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2"); + openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); @@ -165,17 +168,17 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { if (_adminAddress == address(0)) { vm.expectRevert(); vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); } else { vm.expectEmit(true, true, false, true); emit AccountCreated(account2, _adminAddress); // Deploy a managed account to the counterfactual address vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); @@ -196,8 +199,9 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address account2 = openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); assertEq(account2.code.length, 0); - bytes memory initCallData = - abi.encodeWithSignature("createAccountWithNonce(address,bytes32)", accountAdmin, bytes32("2")); + bytes memory initCallData = abi.encodeWithSignature( + "createAccountWithNonce(address,bytes32,address[])", accountAdmin, bytes32("2"), initialGuardians + ); bytes memory initCode = abi.encodePacked(abi.encodePacked(address(openfortFactory)), initCallData); UserOperation[] memory userOpCreateAccount = diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index fd05b32..7f5d2bb 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -26,6 +26,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UpgradeableOpenfortAccount public upgradeableOpenfortAccountImpl; UpgradeableOpenfortFactory public openfortFactory; + address[] initialGuardians; + /** * @notice Initialize the UpgradeableOpenfortAccount testing contract. * Scenario: @@ -43,6 +45,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.deal(accountAdmin, 100 ether); (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); + initialGuardians = [OPENFORT_GUARDIAN]; + vm.startPrank(factoryAdmin); // If we are in a fork if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { @@ -68,12 +72,10 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN - ); + LOCK_PERIOD); // Create an upgradeable account wallet and get its address - accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1"); + accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1", initialGuardians); // deploy a new TestCounter testCounter = new TestCounter{salt: versionSalt}(); @@ -130,7 +132,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, - OPENFORT_GUARDIAN + initialGuardians ); } @@ -148,11 +150,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Deploy a upgradeable account to the counterfactual address vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2"); + openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2"); + openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); @@ -171,18 +173,18 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { if (_adminAddress == address(0)) { vm.expectRevert(); vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); } else { vm.expectEmit(true, true, false, true); emit AccountCreated(accountAddress2, _adminAddress); // Deploy a upgradeable account to the counterfactual address vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); assertEq(accountAddress2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); @@ -202,8 +204,9 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address account2 = openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); assertEq(account2.code.length, 0); - bytes memory initCallData = - abi.encodeWithSignature("createAccountWithNonce(address,bytes32)", accountAdmin, bytes32("2")); + bytes memory initCallData = abi.encodeWithSignature( + "createAccountWithNonce(address,bytes32,address[])", accountAdmin, bytes32("2"), initialGuardians + ); bytes memory initCode = abi.encodePacked(abi.encodePacked(address(openfortFactory)), initCallData); UserOperation[] memory userOpCreateAccount = @@ -1193,12 +1196,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN - ); + LOCK_PERIOD); // Create an upgradeable account wallet using the old EntryPoint and get its address - address payable oldAccountAddress = payable(openfortFactoryOld.createAccountWithNonce(accountAdmin, "999")); + address payable oldAccountAddress = + payable(openfortFactoryOld.createAccountWithNonce(accountAdmin, "999", initialGuardians)); IUpgradeableOpenfortAccount upgradeableAccount = IUpgradeableOpenfortAccount(oldAccountAddress); assertEq(address(upgradeableAccount.entryPoint()), oldEntryPoint); @@ -2676,7 +2678,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Verify that the totalSupply is still 0 assertEq(mockERC20.totalSupply(), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2"); + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, @@ -2727,7 +2729,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testTransferERC721BetweenAccounts() public { assertEq(mockERC721.balanceOf(accountAddress), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2"); + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, @@ -2792,7 +2794,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testTransferERC1155BetweenAccounts() public { assertEq(mockERC1155.balanceOf(accountAddress, 1), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2"); + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index 0fc1b8e..eb01cb9 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -34,6 +34,8 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { event PostOpGasUpdated(uint256 oldPostOpGas, uint256 _newPostOpGas); + address[] initialGuardians; + /* * Auxiliary function to generate a userOP */ @@ -170,6 +172,8 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { vm.deal(paymasterAdmin, 100 ether); (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); + initialGuardians = [OPENFORT_GUARDIAN]; + // If we are in a fork if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); @@ -203,8 +207,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN + LOCK_PERIOD ); // deploy a new TestCounter testCounter = new TestCounter(); @@ -214,7 +217,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create an Openfort account and get its address vm.prank(factoryAdmin); - accountAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); + accountAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1", initialGuardians); } /* From 847c94392a923cdb6fa3fc8ea6a675191d3fb9f1 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 28 Nov 2023 12:59:41 +0100 Subject: [PATCH 52/83] Only one (optional) guardian as initial setup --- .../core/base/BaseRecoverableAccount.sol | 21 ++++---- .../core/managed/ManagedOpenfortFactory.sol | 51 ++++++++++++------- .../UpgradeableOpenfortFactory.sol | 37 ++++++++++---- contracts/interfaces/IBaseOpenfortFactory.sol | 2 +- script/deployManagedAccounts.s.sol | 11 ++-- .../managed/ManagedOpenfortAccountTest.t.sol | 49 ++++++++++++------ .../UpgradeableOpenfortAccountTest.t.sol | 35 ++++++------- .../paymaster/OpenfortPaymasterV2Test.t.sol | 9 ++-- 8 files changed, 126 insertions(+), 89 deletions(-) diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index e513516..14824db 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -84,7 +84,6 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg error OngoingRecovery(); error InvalidRecoverySignatures(); error InvalidSignatureAmount(); - error TooManyInitialGuardians(); /* * @notice Initialize the smart contract account. @@ -96,7 +95,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg uint256 _securityPeriod, uint256 _securityWindow, uint256 _lockPeriod, - address[] memory _initialGuardians + address _initialGuardian ) public initializer { if (_defaultAdmin == address(0) || _entrypoint == address(0)) { revert ZeroAddressNotAllowed(); @@ -114,15 +113,12 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg securityWindow = _securityWindow; securityPeriod = _securityPeriod; - uint256 initialGuardiansNumber = _initialGuardians.length; - if (initialGuardiansNumber > 5) revert TooManyInitialGuardians(); - - for (uint256 i = 0; i < initialGuardiansNumber; i++) { - guardiansConfig.guardians.push(_initialGuardians[i]); - guardiansConfig.info[_initialGuardians[i]].exists = true; - guardiansConfig.info[_initialGuardians[i]].index = 0; - guardiansConfig.info[_initialGuardians[i]].pending = 0; - emit GuardianAdded(_initialGuardians[i]); + if (_initialGuardian != address(0)) { + guardiansConfig.guardians.push(_initialGuardian); + guardiansConfig.info[_initialGuardian].exists = true; + guardiansConfig.info[_initialGuardian].index = 0; + guardiansConfig.info[_initialGuardian].pending = 0; + emit GuardianAdded(_initialGuardian); } } @@ -223,6 +219,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg if (isLocked()) revert AccountLocked(); if (owner() == _guardian) revert GuardianCannotBeOwner(); if (isGuardian(_guardian)) revert DuplicatedGuardian(); + if (_guardian == address(0)) revert ZeroAddressNotAllowed(); if ( guardiansConfig.info[_guardian].pending != 0 @@ -248,7 +245,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg guardiansConfig.guardians.push(_guardian); guardiansConfig.info[_guardian].exists = true; - guardiansConfig.info[_guardian].index = guardiansConfig.guardians.length; + guardiansConfig.info[_guardian].index = guardiansConfig.guardians.length - 1; guardiansConfig.info[_guardian].pending = 0; emit GuardianAdded(_guardian); } diff --git a/contracts/core/managed/ManagedOpenfortFactory.sol b/contracts/core/managed/ManagedOpenfortFactory.sol index fc24da8..204a2af 100644 --- a/contracts/core/managed/ManagedOpenfortFactory.sol +++ b/contracts/core/managed/ManagedOpenfortFactory.sol @@ -19,13 +19,16 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { uint256 public securityPeriod; uint256 public securityWindow; uint256 public lockPeriod; - - error TooManyInitialGuardians(); + address public initialGuardian; /** * @dev Emitted when the implementation returned by the beacon is changed. */ event Upgraded(address indexed implementation); + /** + * @dev Emitted when the initial guardian is changed. + */ + event InitialGuardianUpdated(address indexed oldInitialGuardian, address indexed newInitialGuardian); constructor( address _owner, @@ -34,7 +37,8 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { uint256 _recoveryPeriod, uint256 _securityPeriod, uint256 _securityWindow, - uint256 _lockPeriod + uint256 _lockPeriod, + address _initialGuardian ) BaseOpenfortFactory(_owner, _entrypoint, _accountImplementation) { if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { revert InsecurePeriod(); @@ -43,13 +47,21 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { securityPeriod = _securityPeriod; securityWindow = _securityWindow; lockPeriod = _lockPeriod; + if (_initialGuardian == address(0)) revert ZeroAddressNotAllowed(); + initialGuardian = _initialGuardian; _setImplementation(_accountImplementation); } + function updateInitialGuardian(address _newInitialGuardian) external onlyOwner { + if (_newInitialGuardian == address(0)) revert ZeroAddressNotAllowed(); + emit InitialGuardianUpdated(initialGuardian, _newInitialGuardian); + initialGuardian = _newInitialGuardian; + } + /* * @notice Deploy a new account for _admin with a nonce. */ - function createAccountWithNonce(address _admin, bytes32 _nonce, address[] memory _initialGuardians) + function createAccountWithNonce(address _admin, bytes32 _nonce, bool _initializeGuardian) external returns (address account) { @@ -61,18 +73,19 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { emit AccountCreated(account, _admin); account = address(new ManagedOpenfortProxy{salt: salt}(address(this), "")); - uint256 initialGuardiansNumber = _initialGuardians.length; - if (initialGuardiansNumber > 5) revert TooManyInitialGuardians(); - for (uint256 i = 0; i < initialGuardiansNumber; i++) { - if (_initialGuardians[i] == address(0)) revert ZeroAddressNotAllowed(); - } ManagedOpenfortAccount(payable(account)).initialize( - _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, _initialGuardians + _admin, + entrypointContract, + recoveryPeriod, + securityPeriod, + securityWindow, + lockPeriod, + _initializeGuardian ? initialGuardian : address(0) ); } /* - * @notice Return the address of an account that would be deployed with the given admin signer and nonce. + * @notice Return the address of an account that would be deployed with the given _admin signer and _nonce. */ function getAddressWithNonce(address _admin, bytes32 _nonce) public view returns (address) { bytes32 salt = keccak256(abi.encode(_admin, _nonce)); @@ -96,11 +109,11 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { * Requirements: * * - msg.sender must be the owner of the contract. - * - `newImplementation` must be a contract. + * - `_newImplementation` must be a contract. */ - function upgradeTo(address newImplementation) public virtual onlyOwner { - _setImplementation(newImplementation); - emit Upgraded(newImplementation); + function upgradeTo(address _newImplementation) public virtual onlyOwner { + _setImplementation(_newImplementation); + emit Upgraded(_newImplementation); } /** @@ -108,10 +121,10 @@ contract ManagedOpenfortFactory is BaseOpenfortFactory, IBeacon { * * Requirements: * - * - `newImplementation` must be a contract. + * - `_newImplementation` must be a contract. */ - function _setImplementation(address newImplementation) private { - if (!Address.isContract(newImplementation)) revert NotAContract(); - _implementation = newImplementation; + function _setImplementation(address _newImplementation) private { + if (!Address.isContract(_newImplementation)) revert NotAContract(); + _implementation = _newImplementation; } } diff --git a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol index 87c981c..49d1e05 100644 --- a/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol +++ b/contracts/core/upgradeable/UpgradeableOpenfortFactory.sol @@ -17,9 +17,15 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { uint256 public securityPeriod; uint256 public securityWindow; uint256 public lockPeriod; + address public initialGuardian; error TooManyInitialGuardians(); + /** + * @dev Emitted when the initial guardian is changed. + */ + event InitialGuardianUpdated(address indexed oldInitialGuardian, address indexed newInitialGuardian); + constructor( address _owner, address _entrypoint, @@ -27,7 +33,8 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { uint256 _recoveryPeriod, uint256 _securityPeriod, uint256 _securityWindow, - uint256 _lockPeriod + uint256 _lockPeriod, + address _initialGuardian ) BaseOpenfortFactory(_owner, _entrypoint, _accountImplementation) { if (_lockPeriod < _recoveryPeriod || _recoveryPeriod < _securityPeriod + _securityWindow) { revert InsecurePeriod(); @@ -36,12 +43,20 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { securityPeriod = _securityPeriod; securityWindow = _securityWindow; lockPeriod = _lockPeriod; + if (_initialGuardian == address(0)) revert ZeroAddressNotAllowed(); + initialGuardian = _initialGuardian; + } + + function updateInitialGuardian(address _newInitialGuardian) external onlyOwner { + if (_newInitialGuardian == address(0)) revert ZeroAddressNotAllowed(); + emit InitialGuardianUpdated(initialGuardian, _newInitialGuardian); + initialGuardian = _newInitialGuardian; } /* - * @notice Deploy a new account for _admin with a nonce. + * @notice Deploy a new account for _admin with a _nonce. */ - function createAccountWithNonce(address _admin, bytes32 _nonce, address[] memory _initialGuardians) + function createAccountWithNonce(address _admin, bytes32 _nonce, bool _initializeGuardian) external returns (address account) { @@ -52,18 +67,20 @@ contract UpgradeableOpenfortFactory is BaseOpenfortFactory { emit AccountCreated(account, _admin); account = address(new UpgradeableOpenfortProxy{salt: salt}(_implementation, "")); - uint256 initialGuardiansNumber = _initialGuardians.length; - if (initialGuardiansNumber > 5) revert TooManyInitialGuardians(); - for (uint256 i = 0; i < initialGuardiansNumber; i++) { - if (_initialGuardians[i] == address(0)) revert ZeroAddressNotAllowed(); - } + UpgradeableOpenfortAccount(payable(account)).initialize( - _admin, entrypointContract, recoveryPeriod, securityPeriod, securityWindow, lockPeriod, _initialGuardians + _admin, + entrypointContract, + recoveryPeriod, + securityPeriod, + securityWindow, + lockPeriod, + _initializeGuardian ? initialGuardian : address(0) ); } /* - * @notice Return the address of an account that would be deployed with the given admin signer and nonce. + * @notice Return the address of an account that would be deployed with the given _admin signer and _nonce. */ function getAddressWithNonce(address _admin, bytes32 _nonce) public view returns (address) { bytes32 salt = keccak256(abi.encode(_admin, _nonce)); diff --git a/contracts/interfaces/IBaseOpenfortFactory.sol b/contracts/interfaces/IBaseOpenfortFactory.sol index a9d6d42..1847fcb 100644 --- a/contracts/interfaces/IBaseOpenfortFactory.sol +++ b/contracts/interfaces/IBaseOpenfortFactory.sol @@ -12,7 +12,7 @@ interface IBaseOpenfortFactory { error NotAContract(); /// @notice Deploys a new Account for admin. - function createAccountWithNonce(address _admin, bytes32 _nonce, address[] memory _initialGuardians) + function createAccountWithNonce(address _admin, bytes32 _nonce, bool _initializeGuardian) external returns (address account); diff --git a/script/deployManagedAccounts.s.sol b/script/deployManagedAccounts.s.sol index 3c55b9b..3858c12 100644 --- a/script/deployManagedAccounts.s.sol +++ b/script/deployManagedAccounts.s.sol @@ -7,7 +7,7 @@ import {ManagedOpenfortAccount} from "../contracts/core/managed/ManagedOpenfortA import {ManagedOpenfortFactory} from "../contracts/core/managed/ManagedOpenfortFactory.sol"; contract ManagedOpenfortDeploy is Script { - uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_MAINNET"); + uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); address internal deployAddress = vm.addr(deployPrivKey); IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); @@ -15,13 +15,11 @@ contract ManagedOpenfortDeploy is Script { uint256 private constant SECURITY_PERIOD = 1.5 days; uint256 private constant SECURITY_WINDOW = 0.5 days; uint256 private constant LOCK_PERIOD = 5 days; - address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_MAINNET"); - address[] initialGuardians; + address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_TESTNET"); function run() public { bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); vm.startBroadcast(deployPrivKey); - initialGuardians = [OPENFORT_GUARDIAN]; // Create an acccount to serve as implementation ManagedOpenfortAccount managedOpenfortAccountImpl = new ManagedOpenfortAccount{salt: versionSalt}(); @@ -33,14 +31,15 @@ contract ManagedOpenfortDeploy is Script { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD + LOCK_PERIOD, + OPENFORT_GUARDIAN ); address accountImpl = openfortFactory.implementation(); console.log("Account implementation: ", accountImpl); // Create an managed account wallet and get its address - address firstAccountAddress = openfortFactory.createAccountWithNonce(deployAddress, "1", initialGuardians); + address firstAccountAddress = openfortFactory.createAccountWithNonce(deployAddress, "1", true); console.log(firstAccountAddress); console.log("First Account Address: ", firstAccountAddress); diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index e3b7f75..becd60e 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -24,8 +24,6 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ManagedOpenfortAccount public managedOpenfortAccountImpl; ManagedOpenfortFactory public openfortFactory; - address[] initialGuardians; - /** * @notice Initialize the ManagedOpenfortAccount testing contract. * Scenario: @@ -42,8 +40,6 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.deal(accountAdmin, 100 ether); (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); - initialGuardians = [OPENFORT_GUARDIAN]; - vm.startPrank(factoryAdmin); // If we are in a fork if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { @@ -67,10 +63,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD + LOCK_PERIOD, + OPENFORT_GUARDIAN ); // Create a managed account wallet and get its address - accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1", initialGuardians); + accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1", true); // deploy a new TestCounter testCounter = new TestCounter{salt: versionSalt}(); @@ -127,7 +124,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, - initialGuardians + OPENFORT_GUARDIAN ); } @@ -145,11 +142,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Deploy a managed account to the counterfactual address vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); + openfortFactory.createAccountWithNonce(accountAdmin, "2", true); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); + openfortFactory.createAccountWithNonce(accountAdmin, "2", true); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); @@ -168,17 +165,17 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { if (_adminAddress == address(0)) { vm.expectRevert(); vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); } else { vm.expectEmit(true, true, false, true); emit AccountCreated(account2, _adminAddress); // Deploy a managed account to the counterfactual address vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); @@ -199,9 +196,8 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address account2 = openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); assertEq(account2.code.length, 0); - bytes memory initCallData = abi.encodeWithSignature( - "createAccountWithNonce(address,bytes32,address[])", accountAdmin, bytes32("2"), initialGuardians - ); + bytes memory initCallData = + abi.encodeWithSignature("createAccountWithNonce(address,bytes32,bool)", accountAdmin, bytes32("2"), true); bytes memory initCode = abi.encodePacked(abi.encodePacked(address(openfortFactory)), initCallData); UserOperation[] memory userOpCreateAccount = @@ -1271,6 +1267,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); + vm.prank(accountAdmin); + vm.expectRevert(); + openfortAccount.proposeGuardian(address(0)); + // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); @@ -1989,6 +1989,25 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { openfortAccount.confirmGuardianRevocation(friendAccount); } + /* + * + * + */ + function testUpdateInitialGuardian() public { + IBaseRecoverableAccount(payable(accountAddress)).getGuardians(); + // Create a friend EOA + address newInitialGuardian = makeAddr("newInitialGuardian"); + vm.expectRevert("Ownable: caller is not the owner"); + openfortFactory.updateInitialGuardian(newInitialGuardian); + + vm.prank(factoryAdmin); + openfortFactory.updateInitialGuardian(newInitialGuardian); + + address newAccountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "newNewNew", true); + + IBaseRecoverableAccount(payable(newAccountAddress)).getGuardians(); + } + /* * Random extra tests to mess up with the logic */ diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 7f5d2bb..3d0ad2e 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -26,8 +26,6 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UpgradeableOpenfortAccount public upgradeableOpenfortAccountImpl; UpgradeableOpenfortFactory public openfortFactory; - address[] initialGuardians; - /** * @notice Initialize the UpgradeableOpenfortAccount testing contract. * Scenario: @@ -45,8 +43,6 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.deal(accountAdmin, 100 ether); (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); - initialGuardians = [OPENFORT_GUARDIAN]; - vm.startPrank(factoryAdmin); // If we are in a fork if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { @@ -72,10 +68,10 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD); + LOCK_PERIOD, OPENFORT_GUARDIAN); // Create an upgradeable account wallet and get its address - accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1", initialGuardians); + accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1", true); // deploy a new TestCounter testCounter = new TestCounter{salt: versionSalt}(); @@ -132,7 +128,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, - initialGuardians + OPENFORT_GUARDIAN ); } @@ -150,11 +146,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Deploy a upgradeable account to the counterfactual address vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); + openfortFactory.createAccountWithNonce(accountAdmin, "2", true); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); + openfortFactory.createAccountWithNonce(accountAdmin, "2", true); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); @@ -173,18 +169,18 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { if (_adminAddress == address(0)) { vm.expectRevert(); vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); } else { vm.expectEmit(true, true, false, true); emit AccountCreated(accountAddress2, _adminAddress); // Deploy a upgradeable account to the counterfactual address vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); // Calling it again should just return the address and not create another account vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(_adminAddress, _nonce, initialGuardians); + openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); // Make sure the counterfactual address has not been altered vm.prank(factoryAdmin); assertEq(accountAddress2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); @@ -204,9 +200,8 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address account2 = openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); assertEq(account2.code.length, 0); - bytes memory initCallData = abi.encodeWithSignature( - "createAccountWithNonce(address,bytes32,address[])", accountAdmin, bytes32("2"), initialGuardians - ); + bytes memory initCallData = + abi.encodeWithSignature("createAccountWithNonce(address,bytes32,bool)", accountAdmin, bytes32("2"), true); bytes memory initCode = abi.encodePacked(abi.encodePacked(address(openfortFactory)), initCallData); UserOperation[] memory userOpCreateAccount = @@ -1196,11 +1191,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD); + LOCK_PERIOD, OPENFORT_GUARDIAN); // Create an upgradeable account wallet using the old EntryPoint and get its address address payable oldAccountAddress = - payable(openfortFactoryOld.createAccountWithNonce(accountAdmin, "999", initialGuardians)); + payable(openfortFactoryOld.createAccountWithNonce(accountAdmin, "999", true)); IUpgradeableOpenfortAccount upgradeableAccount = IUpgradeableOpenfortAccount(oldAccountAddress); assertEq(address(upgradeableAccount.entryPoint()), oldEntryPoint); @@ -2678,7 +2673,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Verify that the totalSupply is still 0 assertEq(mockERC20.totalSupply(), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", true); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, @@ -2729,7 +2724,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testTransferERC721BetweenAccounts() public { assertEq(mockERC721.balanceOf(accountAddress), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", true); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, @@ -2794,7 +2789,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testTransferERC1155BetweenAccounts() public { assertEq(mockERC1155.balanceOf(accountAddress, 1), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", initialGuardians); + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", true); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index eb01cb9..6e13ffa 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -34,8 +34,6 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { event PostOpGasUpdated(uint256 oldPostOpGas, uint256 _newPostOpGas); - address[] initialGuardians; - /* * Auxiliary function to generate a userOP */ @@ -172,8 +170,6 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { vm.deal(paymasterAdmin, 100 ether); (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); - initialGuardians = [OPENFORT_GUARDIAN]; - // If we are in a fork if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); @@ -207,7 +203,8 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD + LOCK_PERIOD, + OPENFORT_GUARDIAN ); // deploy a new TestCounter testCounter = new TestCounter(); @@ -217,7 +214,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create an Openfort account and get its address vm.prank(factoryAdmin); - accountAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1", initialGuardians); + accountAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1", true); } /* From 3bd17e9669383a283b6e56ae43a274983fd9a109 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 28 Nov 2023 16:37:20 +0100 Subject: [PATCH 53/83] Removed SimpleAccount info --- README.md | 40 +- .../erc6551/EIP6551OpenfortBenchmark.t.sol | 602 ------------------ 2 files changed, 9 insertions(+), 633 deletions(-) delete mode 100644 test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol diff --git a/README.md b/README.md index e42c94a..3f33809 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Use the `lcov` report format and `genhtml` to view the coverage data in a nice w ./script/gasProfile.sh ``` -### Deploy Static and Upgradeable factories to all chains +### Deploy Upgradeable and Managed factories to all chains ``` ./script/deployAllChains.sh @@ -84,31 +84,17 @@ Use the `lcov` report format and `genhtml` to view the coverage data in a nice w forge script CheckDeposits --force ``` -### Deploy one static factory and one account - -Simulation: - -``` -forge script StaticOpenfortDeploy --fork-url $ -``` - -Actual deployment: - -``` -forge script StaticOpenfortDeploy --fork-url $ --verify --broadcast -``` - ### Deploy one upgradeable factory and one account Simulation: ``` -forge script UpgradeableOpenfortDeploy --fork-url $ +forge script --force script/deployManagedAccounts.s.sol -vvvvv --optimizer-runs 1000000 --slow --fork-url $ ``` Actual deployment: ``` -forge script UpgradeableOpenfortDeploy --fork-url $ --verify --broadcast +forge script --force script/deployManagedAccounts.s.sol -vvvvv --optimizer-runs 1000000 --slow --fork-url $ --broadcast --verify --etherscan-api-key $ ``` ### Compare gas costs @@ -147,14 +133,6 @@ If you run into the error `ImportError: cannot import name 'getargspec' from 'in As of June 2023, the current average gas cost for deploying or using the different smart contracts of this project is: -### Static Accounts -| Smart Contract | Description | # of deployments per game/ecosystem | Avg gas cost | -| :----------------- | :---------------------------------- | :---------------------------------- | :------------------------ | -| StaticOpenfortFactory | Deploy factory (containing StaticOpenfortAccount's implementation) | 1 | ~2,500,000 | -| StaticOpenfortAccount | Create a new static account using the `createAccountWithNonce()` of the factory | indefinite | ~150,000 | -| StaticOpenfortAccount | Updating the EntryPoint address using `updateEntryPoint()` | indefinite | ~1,500 | -| StaticOpenfortAccount | Transferring the ownership using `transferOwnership()` | indefinite | ~25,000 | - ### Upgradeable Accounts | Smart Contract | Description | # of deployments per game/ecosystem | Avg gas cost | | :----------------- | :---------------------------------- | :---------------------------------- | :------------------------ | @@ -171,9 +149,9 @@ As of June 2023, the current average gas cost for deploying or using the differe ## Gas Stats in USD The gas price range is reported as the daily average gas price for the first 90 days of 2023 ± one standard deviation. -| Blockchain | Gas Price Range | Token Price | Create an Static account | Create an Upgradeable account | -| :------------- | :-------------------- | :---------------- | :------ | :------ | -| Ethereum | 30.5 ± 10.5 gwei | ~$1800 | $5.5-$11 | $7.5-15 | -| Polygon | 220 ± 108 gwei | ~$0.67 | $0.01-$0.035 | $0.015-$0.045 | -| Avalanche | 36 ± 4.5 nAVAX | ~$12.8 | $0.06-0.08 | $0.08-$0.11 | -| BSC | 7 ± 0.55 gwei | ~$240 | $0.24-$0.28 | $0.32-$0.36| +| Blockchain | Gas Price Range | Token Price | Create an Upgradeable account | +| :------------- | :-------------------- | :---------------- | :------ | +| Ethereum | 30.5 ± 10.5 gwei | ~$1800 | $7.5-15 | +| Polygon | 220 ± 108 gwei | ~$0.67 | $0.015-$0.045 | +| Avalanche | 36 ± 4.5 nAVAX | ~$12.8 | $0.08-$0.11 | +| BSC | 7 ± 0.55 gwei | ~$240 | $0.32-$0.36 | diff --git a/test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol b/test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol deleted file mode 100644 index 363a800..0000000 --- a/test/foundry/core/erc6551/EIP6551OpenfortBenchmark.t.sol +++ /dev/null @@ -1,602 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -import {Test, console} from "lib/forge-std/src/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {MockERC721} from "contracts/mock/MockERC721.sol"; -import {MockERC20} from "contracts/mock/MockERC20.sol"; - -import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; -import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; - -import {ERC6551Registry} from "lib/erc6551/src/ERC6551Registry.sol"; -import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; - -// contract ERC6551OpenfortBenchmark is Test { -// using ECDSA for bytes32; - -// EntryPoint public entryPoint; - -// UpgradeableOpenfortAccount public implUpgradeableOpenfortAccount; -// UpgradeableOpenfortFactory public upgradeableOpenfortFactory; -// UpgradeableOpenfortAccount public upgradeableOpenfortAccount; - -// ERC6551Registry public erc6551Registry; -// ERC6551OpenfortAccount public erc6551OpenfortAccount; -// ERC6551OpenfortAccount implERC6551OpenfortAccount; - -// uint256 public chainId; - -// MockERC721 nft721; -// USDC testUSDC; - -// // Testing addresses -// address private factoryAdmin; -// uint256 private factoryAdminPKey; - -// address private accountAdmin; -// uint256 private accountAdminPKey; - -// address public upgradeableOpenfortAddressComplex; -// UpgradeableOpenfortAccount public upgradeableOpenfortAccountComplex; - -// address public erc6551OpenfortAddressComplex; -// ERC6551OpenfortAccount public erc6551OpenfortAccountComplex; - -// address payable private beneficiary = payable(makeAddr("beneficiary")); - -// event AccountCreated( -// address account, address implementation, uint256 chainId, address tokenContract, uint256 tokenId, uint256 salt -// ); - -// /* -// * Auxiliary function to generate a userOP -// */ -// function _setupUserOp( -// address sender, -// uint256 _signerPKey, -// bytes memory _initCode, -// bytes memory _callDataForEntrypoint -// ) internal returns (UserOperation[] memory ops) { -// uint256 nonce = entryPoint.getNonce(sender, 0); - -// // Get user op fields -// UserOperation memory op = UserOperation({ -// sender: sender, -// nonce: nonce, -// initCode: _initCode, -// callData: _callDataForEntrypoint, -// callGasLimit: 500_000, -// verificationGasLimit: 500_000, -// preVerificationGas: 500_000, -// maxFeePerGas: 0, -// maxPriorityFeePerGas: 0, -// paymasterAndData: bytes(""), -// signature: bytes("") -// }); - -// // Sign UserOp -// bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); -// bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); -// bytes memory userOpSignature = abi.encodePacked(r, s, v); - -// address recoveredSigner = ECDSA.recover(msgHash, v, r, s); -// address expectedSigner = vm.addr(_signerPKey); -// assertEq(recoveredSigner, expectedSigner); - -// op.signature = userOpSignature; - -// // Store UserOp -// ops = new UserOperation[](1); -// ops[0] = op; -// } - -// /* -// * Auxiliary function to generate a userOP using the execute() -// * from the account -// */ -// function _setupUserOpExecute( -// address sender, -// uint256 _signerPKey, -// bytes memory _initCode, -// address _target, -// uint256 _value, -// bytes memory _callData -// ) internal returns (UserOperation[] memory) { -// bytes memory callDataForEntrypoint = -// abi.encodeWithSignature("execute(address,uint256,bytes)", _target, _value, _callData); - -// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); -// } - -// /* -// * Auxiliary function to generate a userOP using the executeBatch() -// * from the account -// */ -// function _setupUserOpExecuteBatch( -// address sender, -// uint256 _signerPKey, -// bytes memory _initCode, -// address[] memory _target, -// uint256[] memory _value, -// bytes[] memory _callData -// ) internal returns (UserOperation[] memory) { -// bytes memory callDataForEntrypoint = -// abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[])", _target, _value, _callData); - -// return _setupUserOp(sender, _signerPKey, _initCode, callDataForEntrypoint); -// } - -// /** -// * @notice Initialize the UpgradeableOpenfortAccount testing contract. -// * Scenario: -// * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory -// * - accountAdmin is the account used to deploy new static accounts -// * - entryPoint is the singleton EntryPoint -// * - testCounter is the counter used to test userOps -// */ -// function setUp() public { -// // Setup and fund signers -// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); -// vm.deal(factoryAdmin, 100 ether); -// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); -// vm.deal(accountAdmin, 100 ether); - -// uint256 auxChainId; -// assembly { -// auxChainId := chainid() -// } - -// chainId = auxChainId; - -// vm.startPrank(factoryAdmin); - -// // If we are in a fork -// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { -// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); -// } -// // If not a fork, deploy entryPoint (at correct address) -// else { -// EntryPoint entryPoint_aux = new EntryPoint(); -// bytes memory code = address(entryPoint_aux).code; -// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); -// vm.etch(targetAddr, code); -// entryPoint = EntryPoint(payable(targetAddr)); -// } - -// // deploy upgradeable account implementation -// implUpgradeableOpenfortAccount = new UpgradeableOpenfortAccount(); -// // deploy upgradeable account factory -// upgradeableOpenfortFactory = new UpgradeableOpenfortFactory( -// payable(address(entryPoint)), -// address(implUpgradeableOpenfortAccount) -// ); - -// // Create an upgradeable account wallet and get its address -// address upgradeableOpenfortAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1"); - -// upgradeableOpenfortAccount = UpgradeableOpenfortAccount(payable(upgradeableOpenfortAddress)); - -// // deploy a new MockERC721 collection -// nft721 = new MockERC721(); - -// implERC6551OpenfortAccount = new ERC6551OpenfortAccount(); - -// erc6551Registry = new ERC6551Registry(); - -// address erc6551OpenfortAccountAddress = -// erc6551Registry.createAccount(address(implERC6551OpenfortAccount), chainId, address(nft721), 1, 1, ""); - -// erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress)); -// erc6551OpenfortAccount.initialize(address(entryPoint)); - -// nft721.mint(accountAdmin, 1); - -// testUSDC = new USDC(); -// testUSDC.mint(accountAdmin, 100 ether); - -// // Declarations for complex tests - -// upgradeableOpenfortAddressComplex = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "complex"); -// upgradeableOpenfortAccountComplex = UpgradeableOpenfortAccount(payable(upgradeableOpenfortAddressComplex)); - -// nft721.mint(upgradeableOpenfortAddressComplex, 2); - -// erc6551OpenfortAddressComplex = -// erc6551Registry.createAccount(address(implERC6551OpenfortAccount), chainId, address(nft721), 2, 2, ""); -// erc6551OpenfortAccountComplex = ERC6551OpenfortAccount(payable(erc6551OpenfortAddressComplex)); - -// vm.stopPrank(); -// } - -// /* -// * Create a 2nd Upgradeable account with accountAdmin as the owner -// */ -// function test1CreateUpgradeableAccount() public { -// address upgradeableOpenfortAccountAddress2 = -// upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "2"); -// UpgradeableOpenfortAccount upgradeableOpenfortAccountAccount2 = -// UpgradeableOpenfortAccount(payable(upgradeableOpenfortAccountAddress2)); -// IEntryPoint e = upgradeableOpenfortAccountAccount2.entryPoint(); -// assertEq(address(e), address(entryPoint)); -// } - -// /* -// * Create a 2nd ERC6551 account with nft721 as the owner and initialize later -// */ -// function test1CreateERC6551AccountInitAfter() public { -// address erc6551OpenfortAccountAddress2 = -// erc6551Registry.createAccount(address(implERC6551OpenfortAccount), chainId, address(nft721), 1, 2, ""); - -// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); -// erc6551OpenfortAccount2.initialize(address(entryPoint)); -// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); -// assertEq(address(e), address(entryPoint)); -// } - -// /* -// * Create a 2nd ERC6551 account with nft721 as the owner and initialize during creation -// */ -// function test1CreateERC6551AccountInitDuringCreation() public { -// address erc6551OpenfortAccountAddress2 = erc6551Registry.createAccount( -// address(implERC6551OpenfortAccount), -// chainId, -// address(nft721), -// 3, -// 1, -// abi.encodeWithSignature("initialize(address)", address(entryPoint)) -// ); -// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(erc6551OpenfortAccountAddress2)); -// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); -// assertEq(address(e), address(entryPoint)); -// } - -// /* -// * Test owner() function. -// * Check that the owner of the upgradeable account is accountAdmin -// */ -// function test2OwnerUpgradeable() public { -// assertEq(upgradeableOpenfortAccount.owner(), accountAdmin); -// } - -// /* -// * Test owner() function. -// * Check that the owner of the erc6551 account is the owner of the NFT -// */ -// function test2OwnerERC6551() public { -// assertEq(erc6551OpenfortAccount.owner(), nft721.ownerOf(1)); -// } - -// /* -// * Test transferOwnership() function using upgradeable accounts. -// */ -// function test3TransferOwnerUpgradeable() public { -// assertEq(upgradeableOpenfortAccount.owner(), accountAdmin); - -// vm.prank(accountAdmin); -// upgradeableOpenfortAccount.transferOwnership(factoryAdmin); - -// assertEq(upgradeableOpenfortAccount.owner(), accountAdmin); -// assertEq(upgradeableOpenfortAccount.pendingOwner(), factoryAdmin); - -// vm.prank(factoryAdmin); -// upgradeableOpenfortAccount.acceptOwnership(); - -// assertEq(upgradeableOpenfortAccount.owner(), factoryAdmin); -// } - -// /* -// * Test transferOwnership() function using ERC6551 accounts. -// */ -// function test3TransferOwnerERC6551() public { -// assertEq(erc6551OpenfortAccount.owner(), accountAdmin); - -// vm.prank(accountAdmin); -// nft721.safeTransferFrom(accountAdmin, factoryAdmin, 1); - -// assertEq(erc6551OpenfortAccount.owner(), factoryAdmin); -// } - -// /* -// * Test transferOwnership() function using ERC6551 account with a userOp -// * It will fail because the msg.sender doing the transferFrom() is the EntryPoint -// * the user should do it with a regular TX or approving the EntryPoint to spend -// */ -// function testFailTransferOwnerERC6551UserOp() public { -// assertEq(erc6551OpenfortAccount.owner(), accountAdmin); - -// address _target = address(nft721); -// bytes memory _callData = -// abi.encodeWithSignature("transferFrom(address,address,uint256)", accountAdmin, factoryAdmin, 1); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// address(erc6551OpenfortAccount), -// accountAdminPKey, -// bytes(""), -// address(nft721), -// 0, -// abi.encodeWithSignature("execute(address,uint256,bytes)", _target, 0, _callData) -// ); - -// entryPoint.depositTo{value: 1 ether}(address(erc6551OpenfortAccount)); - -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// assertEq(erc6551OpenfortAccount.owner(), factoryAdmin); -// } - -// /* -// * Test transfer funds using upgradeable accounts. -// */ -// function test4TransferFundsUpgradeable() public { -// address upgradeableOpenfortAddress = payable(address((upgradeableOpenfortAccount))); -// console.log("Admin balance: ", accountAdmin.balance); -// console.log("Upgradeable Openfort Account balance: ", upgradeableOpenfortAddress.balance); - -// vm.prank(accountAdmin); -// (bool ok,) = upgradeableOpenfortAddress.call{value: 50 ether}(""); -// assert(ok); -// console.log("Admin balance: ", accountAdmin.balance); -// console.log("Upgradeable Openfort Account balance: ", upgradeableOpenfortAddress.balance); - -// vm.prank(accountAdmin); -// upgradeableOpenfortAccount.execute(accountAdmin, 40 ether, ""); - -// console.log("Admin balance: ", accountAdmin.balance); -// console.log("Upgradeable Openfort Account balance: ", upgradeableOpenfortAddress.balance); -// } - -// /* -// * Test transfer funds function using ERC6551 accounts. -// */ -// function test4TransferFundsERC6551() public { -// address erc6551OpenfortAddress = payable(address((erc6551OpenfortAccount))); -// console.log("Admin balance: ", accountAdmin.balance); -// console.log("ERC6551 Openfort Account balance: ", erc6551OpenfortAddress.balance); - -// vm.prank(accountAdmin); -// (bool ok,) = erc6551OpenfortAddress.call{value: 50 ether}(""); -// assert(ok); -// console.log("Admin balance: ", accountAdmin.balance); -// console.log("ERC6551 Openfort Account balance: ", erc6551OpenfortAddress.balance); - -// vm.prank(accountAdmin); -// erc6551OpenfortAccount.execute(accountAdmin, 40 ether, ""); - -// console.log("Admin balance: ", accountAdmin.balance); -// console.log("ERC6551 Openfort Account balance: ", erc6551OpenfortAddress.balance); -// } - -// /* -// * Test transfer ERC20 using upgradeable accounts. -// */ -// function test5TransferERC20Upgradeable() public { -// address upgradeableOpenfortAddress = payable(address((upgradeableOpenfortAccount))); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - -// vm.prank(accountAdmin); -// testUSDC.transfer(upgradeableOpenfortAddress, 50 ether); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - -// vm.prank(accountAdmin); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 50 ether) -// ); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); -// } - -// /* -// * Test transfer ERC20 function using ERC6551 accounts. -// */ -// function test5TransferERC20ERC6551() public { -// address erc6551OpenfortAddress = payable(address((erc6551OpenfortAccount))); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); - -// vm.prank(accountAdmin); -// testUSDC.transfer(erc6551OpenfortAddress, 50 ether); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("ERC6551 Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); - -// vm.prank(accountAdmin); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 50 ether) -// ); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("ERC6551 Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); -// } - -// /* -// * Test multiple transfers ERC20 using upgradeable accounts. -// */ -// function test6TransferMultipleERC20Upgradeable() public { -// address upgradeableOpenfortAddress = payable(address((upgradeableOpenfortAccount))); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - -// vm.prank(accountAdmin); -// testUSDC.transfer(upgradeableOpenfortAddress, 50 ether); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); - -// vm.startPrank(accountAdmin); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// upgradeableOpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// vm.stopPrank(); - -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(upgradeableOpenfortAddress)); -// } - -// /* -// * Test multiple transfers ERC20 function using ERC6551 accounts. -// */ -// function test6TransferMultipleERC20ERC6551() public { -// address erc6551OpenfortAddress = payable(address((erc6551OpenfortAccount))); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("Upgradeable Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); - -// vm.prank(accountAdmin); -// testUSDC.transfer(erc6551OpenfortAddress, 50 ether); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("ERC6551 Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); - -// vm.startPrank(accountAdmin); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// erc6551OpenfortAccount.execute( -// address(testUSDC), 0, abi.encodeWithSignature("transfer(address,uint256)", accountAdmin, 5 ether) -// ); -// vm.stopPrank(); -// console.log("Admin balance: ", testUSDC.balanceOf(accountAdmin)); -// console.log("ERC6551 Openfort Account balance: ", testUSDC.balanceOf(erc6551OpenfortAddress)); -// } - -// /* -// * Test owner() function. -// * Check that the owner of the erc6551 account is the owner of the NFT -// */ -// function test7ComplexOwner() public { -// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); -// } - -// /* -// * Test transferOwnership() function using upgradeable account that have ERC6551 accounts. -// * Scenario: a complex account changes the ownership; all NFTs are manageable by the new owner -// */ -// function test8TransferOwner4337Complex() public { -// // The EOA is the owner of the Upgradeable account -// assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); - -// // The upgradeable account is the owner of the ERC6551 accounts because it holds the NFT -// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); -// assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); - -// vm.prank(accountAdmin); -// upgradeableOpenfortAccountComplex.transferOwnership(factoryAdmin); - -// assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); -// assertEq(upgradeableOpenfortAccountComplex.pendingOwner(), factoryAdmin); - -// vm.prank(factoryAdmin); -// upgradeableOpenfortAccountComplex.acceptOwnership(); - -// assertEq(upgradeableOpenfortAccountComplex.owner(), factoryAdmin); -// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); -// assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); -// } - -// /* -// * Test transferOwnership() function using upgradeable account that have ERC6551 accounts. -// * Scenario: a complex account transfer an NFT to send an ERC6551 account to another user -// */ -// function test9TransferOwnerERC6551Complex() public { -// // The EOA is the owner of the Upgradeable account -// assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); -// // The upgradeable account is the owner of the ERC6551 accounts because it holds the NFT -// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); -// assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); - -// vm.prank(accountAdmin); -// upgradeableOpenfortAccountComplex.execute( -// address(nft721), -// 0, -// abi.encodeWithSignature( -// "transferFrom(address,address,uint256)", upgradeableOpenfortAddressComplex, factoryAdmin, 2 -// ) -// ); -// assertEq(nft721.ownerOf(2), factoryAdmin); -// assertEq(erc6551OpenfortAccountComplex.owner(), factoryAdmin); -// } - -// /* -// * Test transferOwnership() function using upgradeable account that have ERC6551 accounts. -// */ -// function test9TransferOwnerERC6551ComplexUserOp() public { -// // The EOA is the owner of the Upgradeable account -// assertEq(upgradeableOpenfortAccountComplex.owner(), accountAdmin); -// // The upgradeable account is the owner of the ERC6551 accounts because it holds the NFT -// assertEq(erc6551OpenfortAccountComplex.owner(), upgradeableOpenfortAddressComplex); -// assertEq(nft721.ownerOf(2), upgradeableOpenfortAddressComplex); - -// UserOperation[] memory userOp = _setupUserOpExecute( -// upgradeableOpenfortAddressComplex, -// accountAdminPKey, -// bytes(""), -// address(nft721), -// 0, -// abi.encodeWithSignature( -// "safeTransferFrom(address,address,uint256)", upgradeableOpenfortAddressComplex, factoryAdmin, 2 -// ) -// ); - -// entryPoint.depositTo{value: 1 ether}(upgradeableOpenfortAddressComplex); -// vm.expectRevert(); -// entryPoint.simulateValidation(userOp[0]); -// entryPoint.handleOps(userOp, beneficiary); - -// assertEq(nft721.ownerOf(2), factoryAdmin); -// assertEq(erc6551OpenfortAccountComplex.owner(), factoryAdmin); -// } -// } From 3c861f627acee852c210c5e6d6c25d0ea93b7a37 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 28 Nov 2023 16:48:25 +0100 Subject: [PATCH 54/83] Run fmt --- .../core/upgradeable/UpgradeableOpenfortAccountTest.t.sol | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 3d0ad2e..71d3b72 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -68,7 +68,9 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD, OPENFORT_GUARDIAN); + LOCK_PERIOD, + OPENFORT_GUARDIAN + ); // Create an upgradeable account wallet and get its address accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1", true); @@ -1191,7 +1193,9 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, - LOCK_PERIOD, OPENFORT_GUARDIAN); + LOCK_PERIOD, + OPENFORT_GUARDIAN + ); // Create an upgradeable account wallet using the old EntryPoint and get its address address payable oldAccountAddress = From 8bc8030f6e9c8736a35a6f5f995e2269d03e2544 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 29 Nov 2023 12:38:04 +0100 Subject: [PATCH 55/83] Raw commands in case bytecode does not match --- script/RawTXDeployAccFact-0.5.0.sh | 1 + script/RawTXDeployAccImpl-0.5.0.sh | 1 + script/RawVerifyAcc-0.5.0.sh | 1 + script/RawVerifyFact-0.5.0.sh | 1 + 4 files changed, 4 insertions(+) create mode 100644 script/RawTXDeployAccFact-0.5.0.sh create mode 100644 script/RawTXDeployAccImpl-0.5.0.sh create mode 100644 script/RawVerifyAcc-0.5.0.sh create mode 100644 script/RawVerifyFact-0.5.0.sh diff --git a/script/RawTXDeployAccFact-0.5.0.sh b/script/RawTXDeployAccFact-0.5.0.sh new file mode 100644 index 0000000..5f5ab04 --- /dev/null +++ b/script/RawTXDeployAccFact-0.5.0.sh @@ -0,0 +1 @@ +cast send 0x4e59b44847b379578588920ca78fbf26c0b4956c 0x050000000000000000000000000000000000000000000000000000000000005060806040523480156200001157600080fd5b50604051620019fb380380620019fb83398101604081905262000034916200025f565b878787620000423362000189565b6001600160a01b0383166200006a576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0382163b15806200008a57506001600160a01b0381163b155b15620000a9576040516309ee12d560e01b815260040160405180910390fd5b620000b48362000189565b600280546001600160a01b039384166001600160a01b031991821617909155600380549290931691161790555084821080620000fa5750620000f78385620002e3565b85105b156200011957604051631a37e0af60e11b815260040160405180910390fd5b60048590556005849055600683905560078290556001600160a01b03811662000155576040516342bcdf7f60e11b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0383161790556200017b86620001a7565b50505050505050506200030b565b600180546001600160a01b0319169055620001a481620001f2565b50565b6001600160a01b0381163b620001d0576040516309ee12d560e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b03811681146200025a57600080fd5b919050565b600080600080600080600080610100898b0312156200027d57600080fd5b620002888962000242565b97506200029860208a0162000242565b9650620002a860408a0162000242565b9550606089015194506080890151935060a0890151925060c08901519150620002d460e08a0162000242565b90509295985092959890939650565b808201808211156200030557634e487b7160e01b600052601160045260246000fd5b92915050565b6116e0806200031b6000396000f3fe6080604052600436106200015d5760003560e01c80635c60da1b11620000cf5780638da5cb5b116200007d578063c23a5cea1162000060578063c23a5cea14620003a8578063e30c397814620003cd578063f2fde38b14620003fa57600080fd5b80638da5cb5b1462000363578063bb9fe6bf146200039057600080fd5b806379ba509711620000b257806379ba509714620003045780637cc0d906146200031c578063835a4750146200033457600080fd5b80635c60da1b14620002bf578063715018a614620002ec57600080fd5b80632428e6a4116200012d5780633fd8b02f11620001105780633fd8b02f14620002535780634dd01253146200026b5780635b31f150146200029a57600080fd5b80632428e6a414620002095780633659cfe6146200022e57600080fd5b80626fda35146200016257806301295c7a146200018d5780630396cb6014620001d857806316c3e22f14620001f1575b600080fd5b3480156200016f57600080fd5b506200017a60065481565b6040519081526020015b60405180910390f35b3480156200019a57600080fd5b50620001b2620001ac36600462000e2f565b6200041f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200162000184565b620001ef620001e936600462000e5e565b62000525565b005b348015620001fe57600080fd5b506200017a60045481565b3480156200021657600080fd5b50620001ef6200022836600462000e86565b620005bf565b3480156200023b57600080fd5b50620001ef6200024d36600462000e86565b620006a5565b3480156200026057600080fd5b506200017a60075481565b3480156200027857600080fd5b50600254620001b29073ffffffffffffffffffffffffffffffffffffffff1681565b348015620002a757600080fd5b50620001b2620002b936600462000ea6565b620006fe565b348015620002cc57600080fd5b5060035473ffffffffffffffffffffffffffffffffffffffff16620001b2565b348015620002f957600080fd5b50620001ef62000958565b3480156200031157600080fd5b50620001ef62000970565b3480156200032957600080fd5b506200017a60055481565b3480156200034157600080fd5b50600854620001b29073ffffffffffffffffffffffffffffffffffffffff1681565b3480156200037057600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff16620001b2565b3480156200039d57600080fd5b50620001ef62000a2d565b348015620003b557600080fd5b50620001ef620003c736600462000e86565b62000abd565b348015620003da57600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff16620001b2565b3480156200040757600080fd5b50620001ef6200041936600462000e86565b62000b51565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602082015290810182905260009081906060016040516020818303038152906040528051906020012090506200051d81604051806020016200047c9062000dfe565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f9091011660408181523060208301528082015260006060820152608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905262000501929160200162000f23565b6040516020818303038152906040528051906020012062000c04565b949350505050565b6200052f62000c13565b6002546040517f0396cb6000000000000000000000000000000000000000000000000000000000815263ffffffff8316600482015273ffffffffffffffffffffffffffffffffffffffff90911690630396cb609034906024016000604051808303818588803b158015620005a257600080fd5b505af1158015620005b7573d6000803e3d6000fd5b505050505050565b620005c962000c13565b73ffffffffffffffffffffffffffffffffffffffff811662000617576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60085460405173ffffffffffffffffffffffffffffffffffffffff8084169216907fa4e16db2b0cb4dfdd6133a9a8fc627c68f371f4550ae3d0701b5b4068415a7da90600090a3600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b620006af62000c13565b620006ba8162000c96565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6040805173ffffffffffffffffffffffffffffffffffffffff8516602082015290810183905260009081906060016040516020818303038152906040528051906020012090506200075085856200041f565b915073ffffffffffffffffffffffffffffffffffffffff82163b1562000777575062000951565b8473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fac631f3001b55ea1509cf3d7e74898f85392a61a76e8149181ae1259622dabc860405160405180910390a38030604051620007e19062000dfe565b73ffffffffffffffffffffffffffffffffffffffff90911681526040602082018190526000908201526060018190604051809103906000f59050801580156200082e573d6000803e3d6000fd5b5091508173ffffffffffffffffffffffffffffffffffffffff166383a5041c86600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166004546005546006546007548a6200088b576000620008a5565b60085473ffffffffffffffffffffffffffffffffffffffff165b60405160e089901b7fffffffff0000000000000000000000000000000000000000000000000000000016815273ffffffffffffffffffffffffffffffffffffffff9788166004820152958716602487015260448601949094526064850192909252608484015260a483015290911660c482015260e401600060405180830381600087803b1580156200093657600080fd5b505af11580156200094b573d6000803e3d6000fd5b50505050505b9392505050565b6200096262000c13565b6200096e600062000d2c565b565b600154339073ffffffffffffffffffffffffffffffffffffffff16811462000a1f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e6572000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b62000a2a8162000d2c565b50565b62000a3762000c13565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb9fe6bf6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801562000aa257600080fd5b505af115801562000ab7573d6000803e3d6000fd5b50505050565b62000ac762000c13565b6002546040517fc23a5cea00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301529091169063c23a5cea90602401600060405180830381600087803b15801562000b3557600080fd5b505af115801562000b4a573d6000803e3d6000fd5b5050505050565b62000b5b62000c13565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811790915562000bbf60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b60006200095183833062000d5f565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200096e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000a16565b73ffffffffffffffffffffffffffffffffffffffff81163b62000ce5576040517f09ee12d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905562000a2a8162000d89565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61076e8062000f3d83390190565b73ffffffffffffffffffffffffffffffffffffffff8116811462000a2a57600080fd5b6000806040838503121562000e4357600080fd5b823562000e508162000e0c565b946020939093013593505050565b60006020828403121562000e7157600080fd5b813563ffffffff811681146200095157600080fd5b60006020828403121562000e9957600080fd5b8135620009518162000e0c565b60008060006060848603121562000ebc57600080fd5b833562000ec98162000e0c565b9250602084013591506040840135801515811462000ee657600080fd5b809150509250925092565b6000815160005b8181101562000f14576020818501810151868301520162000ef8565b50600093019283525090919050565b60006200051d62000f35838662000ef1565b8462000ef156fe608060405234801561001057600080fd5b5060405161076e38038061076e83398101604081905261002f91610431565b818161003d82826000610046565b5050505061055b565b61004f83610107565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a26000825111806100905750805b1561010257610100836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fa91906104f1565b8361028b565b505b505050565b6001600160a01b0381163b6101715760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b6101e5816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d691906104f1565b6001600160a01b03163b151590565b61024a5760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b6064820152608401610168565b7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5080546001600160a01b0319166001600160a01b0392909216919091179055565b60606102b08383604051806060016040528060278152602001610747602791396102b7565b9392505050565b6060600080856001600160a01b0316856040516102d4919061050c565b600060405180830381855af49150503d806000811461030f576040519150601f19603f3d011682016040523d82523d6000602084013e610314565b606091505b50909250905061032686838387610330565b9695505050505050565b6060831561039f578251600003610398576001600160a01b0385163b6103985760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610168565b50816103a9565b6103a983836103b1565b949350505050565b8151156103c15781518083602001fd5b8060405162461bcd60e51b81526004016101689190610528565b80516001600160a01b03811681146103f257600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015610428578181015183820152602001610410565b50506000910152565b6000806040838503121561044457600080fd5b61044d836103db565b60208401519092506001600160401b038082111561046a57600080fd5b818501915085601f83011261047e57600080fd5b815181811115610490576104906103f7565b604051601f8201601f19908116603f011681019083821181831017156104b8576104b86103f7565b816040528281528860208487010111156104d157600080fd5b6104e283602083016020880161040d565b80955050505050509250929050565b60006020828403121561050357600080fd5b6102b0826103db565b6000825161051e81846020870161040d565b9190910192915050565b602081526000825180602084015261054781604085016020870161040d565b601f01601f19169190910160400192915050565b6101dd8061056a6000396000f3fe6080604052600436106100225760003560e01c80635c60da1b1461003957610031565b366100315761002f610077565b005b61002f610077565b34801561004557600080fd5b5061004e610089565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b610087610082610098565b610146565b565b6000610093610098565b905090565b60006100d87fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d505473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff16635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610122573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610093919061016a565b3660008037600080366000845af43d6000803e808015610165573d6000f35b3d6000fd5b60006020828403121561017c57600080fd5b815173ffffffffffffffffffffffffffffffffffffffff811681146101a057600080fd5b939250505056fea2646970667358221220c4d4b3a4d980bac705fc7a36107036a2b7ca6b6677991f9e52c1add174f22ae464736f6c63430008130033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212201ad2ce6ded5e31cd8da461adad5d9d3290e5dcb9ce15728a23ae54ca005dc59664736f6c634300081300330000000000000000000000009590ed0c18190a310f4e93caccc4cc17270bed400000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278900000000000000000000000036604309934a2fc92c3445cf4566b23b5b4bbaad000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000001fa40000000000000000000000000000000000000000000000000000000000000a8c000000000000000000000000000000000000000000000000000000000000697800000000000000000000000009590ed0c18190a310f4e93caccc4cc17270bed40 --private-key $PK_PAYMASTER_OWNER_TESTNET \ No newline at end of file diff --git a/script/RawTXDeployAccImpl-0.5.0.sh b/script/RawTXDeployAccImpl-0.5.0.sh new file mode 100644 index 0000000..e60173d --- /dev/null +++ b/script/RawTXDeployAccImpl-0.5.0.sh @@ -0,0 +1 @@ +cast send 0x4e59b44847b379578588920ca78fbf26c0b4956c 0x050000000000000000000000000000000000000000000000000000000000005060806040523480156200001157600080fd5b5060405133907fb265863f6bc439675d52789c6b89c2f140baa0e87e233a84fe05b166fc3696ed90600090a2620000476200004d565b6200010e565b600054610100900460ff1615620000ba5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff908116146200010c576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6154e8806200011e6000396000f3fe6080604052600436106102be5760003560e01c806384b0196e1161016e578063bc197c81116100cb578063da4b88d31161007f578063f23a6e6111610064578063f23a6e611461096c578063f2fde38b146109b2578063f83d08ba146109d257600080fd5b8063da4b88d314610921578063e30c39781461094157600080fd5b8063c304555f116100b0578063c304555f146108d7578063c399ec88146108f7578063d087d2881461090c57600080fd5b8063bc197c811461086f578063bd5021af146108b757600080fd5b8063a69df4b511610122578063b0d691fe11610107578063b0d691fe14610735578063b61d27f61461075c578063b7b8d6041461076f57600080fd5b8063a69df4b514610700578063af648c3d1461071557600080fd5b806389b9610d1161015357806389b9610d146105f65780638da5cb5b146106af578063a4e2d634146106e957600080fd5b806384b0196e146105ae57806384f4fc6a146105d657600080fd5b80632db0ef5e1161021c578063715018a6116101d057806379ba5097116101b557806379ba5097146105645780637e5852d91461057957806383a5041c1461058e57600080fd5b8063715018a61461052f578063766a27321461054457600080fd5b80633a871cdd116102015780633a871cdd146104d957806347e1da2a1461050757806354387ad71461051a57600080fd5b80632db0ef5e146104995780632ef1f99b146104b957600080fd5b8063150b7a02116102735780631b19b6b8116102585780631b19b6b8146104395780631e5797c814610459578063201b2e011461047957600080fd5b8063150b7a02146103a35780631626ba7e1461041957600080fd5b80630665f04b116102a45780630665f04b146103265780630ba234d6146103485780630c68ba211461035d57600080fd5b806223de29146102ca57806301ffc9a7146102f157600080fd5b366102c557005b600080fd5b3480156102d657600080fd5b506102ef6102e5366004614480565b5050505050505050565b005b3480156102fd57600080fd5b5061031161030c366004614531565b6109e7565b60405190151581526020015b60405180910390f35b34801561033257600080fd5b5061033b610b17565b60405161031d9190614573565b34801561035457600080fd5b506102ef610bf5565b34801561036957600080fd5b506103116103783660046145cd565b73ffffffffffffffffffffffffffffffffffffffff16600090815260d2602052604090205460ff1690565b3480156103af57600080fd5b506103e86103be3660046145ea565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200161031d565b34801561042557600080fd5b506103e8610434366004614769565b610c61565b34801561044557600080fd5b506103116104543660046147b0565b610e7c565b34801561046557600080fd5b506102ef6104743660046145cd565b6113f2565b34801561048557600080fd5b506102ef6104943660046145cd565b6114eb565b3480156104a557600080fd5b506102ef6104b4366004614860565b6117cb565b3480156104c557600080fd5b506102ef6104d43660046148e8565b611b5d565b3480156104e557600080fd5b506104f96104f436600461492a565b611d70565b60405190815260200161031d565b6102ef61051536600461497e565b611d8f565b34801561052657600080fd5b5060d1546104f9565b34801561053b57600080fd5b506102ef611e70565b34801561055057600080fd5b506102ef61055f3660046145cd565b611e84565b34801561057057600080fd5b506102ef611f7d565b34801561058557600080fd5b506104f961202f565b34801561059a57600080fd5b506102ef6105a9366004614a06565b61204f565b3480156105ba57600080fd5b506105c36124b4565b60405161031d9796959493929190614adc565b3480156105e257600080fd5b506102ef6105f13660046145cd565b612590565b34801561060257600080fd5b5060d45461066e9073ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000810467ffffffffffffffff16907c0100000000000000000000000000000000000000000000000000000000900463ffffffff1683565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845267ffffffffffffffff909216602084015263ffffffff169082015260600161031d565b3480156106bb57600080fd5b506106c4612672565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161031d565b3480156106f557600080fd5b5060d3544210610311565b34801561070c57600080fd5b506102ef612693565b34801561072157600080fd5b506102ef6107303660046145cd565b612721565b34801561074157600080fd5b50735ff137d4b0fdcd49dca30c7cf57e578a026d27896106c4565b6102ef61076a366004614b9b565b6128a0565b34801561077b57600080fd5b5061081961078a3660046145cd565b6035602052600090815260409020805460029091015465ffffffffffff80831692660100000000000081048216926c0100000000000000000000000082049092169160ff720100000000000000000000000000000000000083048116927301000000000000000000000000000000000000009004169073ffffffffffffffffffffffffffffffffffffffff1686565b6040805165ffffffffffff978816815295871660208701529390951692840192909252151560608301521515608082015273ffffffffffffffffffffffffffffffffffffffff90911660a082015260c00161031d565b34801561087b57600080fd5b506103e861088a366004614bf7565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b3480156108c357600080fd5b506102ef6108d23660046145cd565b6128ba565b3480156108e357600080fd5b506102ef6108f23660046145cd565b612aff565b34801561090357600080fd5b506104f9612d69565b34801561091857600080fd5b506104f9612df9565b34801561092d57600080fd5b506102ef61093c3660046145cd565b612e53565b34801561094d57600080fd5b50609a5473ffffffffffffffffffffffffffffffffffffffff166106c4565b34801561097857600080fd5b506103e8610987366004614c95565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b3480156109be57600080fd5b506102ef6109cd3660046145cd565b6131c4565b3480156109de57600080fd5b506102ef613269565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a02000000000000000000000000000000000000000000000000000000001480610a7957507fffffffff0000000000000000000000000000000000000000000000000000000082167e23de2900000000000000000000000000000000000000000000000000000000145b80610ac557507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b80610b1157507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b60d15460609060009067ffffffffffffffff811115610b3857610b3861465d565b604051908082528060200260200182016040528015610b61578160200160208202803683370190505b50905060005b60d154811015610bef5760d1805482908110610b8557610b85614cff565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16828281518110610bc257610bc2614cff565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152600101610b67565b50919050565b610bfd6132ff565b610c076001613385565b60d45460405173ffffffffffffffffffffffffffffffffffffffff9091169081907f8154b6c5e1fc90d44b49808ef93f9739148d0821411890f8cd684385e24b9f1e90600090a2600060d4819055610c5e90613453565b50565b604080517f57159f03b9efda178eab2037b2ec0b51ce11be0051b8a2a9992c29dc260e4a30602082015290810183905260009081906060016040516020818303038152906040528051906020012090506000610cbc8261348d565b90506000610cca82866134d5565b90508073ffffffffffffffffffffffffffffffffffffffff16610ceb612672565b73ffffffffffffffffffffffffffffffffffffffff1603610d3257507f1626ba7e000000000000000000000000000000000000000000000000000000009250610b11915050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260356020526040902080546601000000000000900465ffffffffffff161580610d81575080544265ffffffffffff909116115b80610d9f5750805442660100000000000090910465ffffffffffff16105b80610dc45750805460016c0100000000000000000000000090910465ffffffffffff16105b15610df657507fffffffff000000000000000000000000000000000000000000000000000000009350610b1192505050565b610dfe612672565b600282015473ffffffffffffffffffffffffffffffffffffffff908116911614610e4f57507fffffffff000000000000000000000000000000000000000000000000000000009350610b1192505050565b507f1626ba7e000000000000000000000000000000000000000000000000000000009350610b1192505050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260356020526040812080546601000000000000900465ffffffffffff168203610ec55760009150506113eb565b610ecd612672565b600282015473ffffffffffffffffffffffffffffffffffffffff908116911614610efb5760009150506113eb565b6000601885856003818110610f1257610f12614cff565b909101357fff000000000000000000000000000000000000000000000000000000000000001690911c9050601086866002818110610f5257610f52614cff565b909101357fff000000000000000000000000000000000000000000000000000000000000001690911c9050600887876001818110610f9257610f92614cff565b909101357fff000000000000000000000000000000000000000000000000000000000000001690911c90508787600081610fce57610fce614cff565b9050013560f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916171717905063b61d27f660e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916036111c15781546c01000000000000000000000000900465ffffffffffff16600003611077576000925050506113eb565b81547fffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff81166c010000000000000000000000009182900465ffffffffffff9081167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0116909102178083557201000000000000000000000000000000000000900460ff161561110b576001925050506113eb565b600061111a8560048189614d2e565b8101906111279190614d58565b50909150503073ffffffffffffffffffffffffffffffffffffffff82160361115557600093505050506113eb565b8254730100000000000000000000000000000000000000900460ff1615806111a4575073ffffffffffffffffffffffffffffffffffffffff8116600090815260018401602052604090205460ff165b156111b557600193505050506113eb565b600093505050506113eb565b7fb81e25d6000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008216016113e45760006112198560048189614d2e565b8101906112269190614ec0565b5050805184549192506c0100000000000000000000000090910465ffffffffffff161080611255575060098151115b1561126657600093505050506113eb565b61127081516134f9565b835465ffffffffffff6c01000000000000000000000000808304821693909303169091027fffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff9091161780845560ff720100000000000000000000000000000000000090910416156112e757600193505050506113eb565b60005b81518110156113d7573073ffffffffffffffffffffffffffffffffffffffff1682828151811061131c5761131c614cff565b602002602001015173ffffffffffffffffffffffffffffffffffffffff160361134c5760009450505050506113eb565b8354730100000000000000000000000000000000000000900460ff1680156113bd575083600101600083838151811061138757611387614cff565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff16155b156113cf5760009450505050506113eb565b6001016112ea565b60019450505050506113eb565b6000925050505b9392505050565b6113fa6132ff565b60d354421015611436576040517f6315bfbb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d260205260408120600201549003611497576040517ff3fff9d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600081815260d26020526040808220600201829055517ff02321bd7568c59340f20b0b1d856f1a94647f1eff2e1129116bb92a61c02fa49190a250565b60d354421015611527576040517f6315bfbb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d260205260408120600201549003611588576040517f5d5d3ca000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d260205260409020600201544210156115ea576040517fc39da77f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d05473ffffffffffffffffffffffffffffffffffffffff8216600090815260d260205260409020600201546116209190614fcb565b421115611659576040517fe854eb5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d2602052604090205460ff16156116b9576040517fa3da3fa600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d18054600180820183557f695fb3134ad82c3b8022bc5464edd0bcc9424ef672b52245dcb6ab2374327ce390910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8516908117909155600090815260d26020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001682179055905461176b9190614fde565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260d2602052604080822060018101949094556002909301819055915190917f038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f96991a250565b6117d3613595565b600b8110611842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f57686974656c69737420746f6f2062696700000000000000000000000000000060448201526064015b60405180910390fd5b60005b818110156118fb5773ffffffffffffffffffffffffffffffffffffffff871660009081526035602052604081206001919082019085858581811061188b5761188b614cff565b90506020020160208101906118a091906145cd565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055600101611845565b80156119675773ffffffffffffffffffffffffffffffffffffffff8716600090815260356020526040902080547fffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffff16730100000000000000000000000000000000000000179055611a2d565b8365ffffffffffff1665ffffffffffff036119e15773ffffffffffffffffffffffffffffffffffffffff8716600090815260356020526040902080547fffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffff167201000000000000000000000000000000000000179055611a2d565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260356020526040902080547fffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffff1690555b73ffffffffffffffffffffffffffffffffffffffff87166000908152603560205260409020805465ffffffffffff8681166c01000000000000000000000000027fffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff8983166601000000000000027fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909416928b16929092179290921716179055611ad5612672565b73ffffffffffffffffffffffffffffffffffffffff88811660008181526035602052604080822060020180547fffffffffffffffffffffffff000000000000000000000000000000000000000016959094169490941790925591517f3cdc3555baaf2d5c4f181ba23f1c025870b490640338124786bf208fce3794bd9190a250505050505050565b611b676001613385565b60d45467ffffffffffffffff42811674010000000000000000000000000000000000000000909204161115611bc8576040517f8398fffc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d4547c0100000000000000000000000000000000000000000000000000000000900463ffffffff16611c57576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4e6f20677561726469616e7320736574206f6e2077616c6c65740000000000006044820152606401611839565b60d4547c0100000000000000000000000000000000000000000000000000000000900463ffffffff168114611cb8576040517f2eb0023500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611cc28282613623565b611cf8576040517f5be06cfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d48054600090915573ffffffffffffffffffffffffffffffffffffffff16611d2081613897565b611d2a6000613453565b60405173ffffffffffffffffffffffffffffffffffffffff8216907f46f5008939fa0b4beb46ae7f0e8368318bb72b56608910ca71c23e83382f609f90600090a2505050565b6000611d7a6138c8565b611d848484613945565b90506113eb82613b9c565b611d97613595565b6009851180611da65750848114155b80611db15750848314155b15611de8576040517fc0375efc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015611e6757611e5f878783818110611e0857611e08614cff565b9050602002016020810190611e1d91906145cd565b868684818110611e2f57611e2f614cff565b90506020020135858585818110611e4857611e48614cff565b9050602002810190611e5a9190614ff1565b613c13565b600101611deb565b50505050505050565b611e786132ff565b611e826000613897565b565b611e8c6132ff565b60d354421015611ec8576040517f6315bfbb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d260205260408120600201549003611f29576040517f5d5d3ca000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600081815260d26020526040808220600201829055517f818b21e1ff67c3251b6f0ab5618cf4f91f5ddd13dee16fa7154053d7250d5dd49190a250565b609a54339073ffffffffffffffffffffffffffffffffffffffff168114612026576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e657200000000000000000000000000000000000000000000006064820152608401611839565b610c5e81613897565b600061203c60d354421090565b6120465750600090565b60d3545b905090565b600054610100900460ff161580801561206f5750600054600160ff909116105b806120895750303b158015612089575060005460ff166001145b612115576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401611839565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561217357600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff881615806121aa575073ffffffffffffffffffffffffffffffffffffffff8716155b156121e1576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b858310806121f757506121f48486614fcb565b86105b1561222e576040517f346fc15e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60cc546040805173ffffffffffffffffffffffffffffffffffffffff928316815291891660208301527f9efc8bb238b1965522b3e784290f41e0229e21a34f499fd2f71b64463283a416910160405180910390a161228b88613897565b60cc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8916179055604080518082018252600881527f4f70656e666f72740000000000000000000000000000000000000000000000006020808301919091528251808401909352600383527f302e3500000000000000000000000000000000000000000000000000000000009083015261233c91613c9a565b60cd86905560ce83905560d084905560cf85905573ffffffffffffffffffffffffffffffffffffffff8216156124485760d1805460018082019092557f695fb3134ad82c3b8022bc5464edd0bcc9424ef672b52245dcb6ab2374327ce30180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8516908117909155600081815260d2602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016851781559384018290556002909301819055915190917f038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f96991a25b80156102e557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15050505050505050565b6000606080600080600060606001546000801b1480156124d45750600254155b61253a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4549503731323a20556e696e697469616c697a656400000000000000000000006044820152606401611839565b612542613d3f565b61254a613dd1565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b612598613595565b73ffffffffffffffffffffffffffffffffffffffff81166000908152603560205260409020546601000000000000900465ffffffffffff1615610c5e5773ffffffffffffffffffffffffffffffffffffffff811660008181526035602052604080822080547fffffffffffffffffffffffffff00ffffffffffff000000000000ffffffffffff16815560020180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055517f17c796fb82086b3c9effaec517342e5ca9ed8fd78c339137ec082f748ab60cbe9190a250565b600061204a60685473ffffffffffffffffffffffffffffffffffffffff1690565b33600090815260d2602052604090205460ff166126dc576040517f0da3c81800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d3544210612717576040517f8c99a3a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e826000613453565b6127296132ff565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d2602052604090205460ff16612788576040517f0da3c81800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d26020526040902060020154158015906127f3575060d05473ffffffffffffffffffffffffffffffffffffffff8216600090815260d260205260409020600201546127f09190614fcb565b42105b1561282a576040517f430361fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60cf546128379042614fcb565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260d26020526040908190206002018390555190917f66dd255378f16b98cb793e105d71a1c86f943abe088d6655477c7505fad15e6b9161289591815260200190565b60405180910390a250565b6128a8613595565b6128b484848484613c13565b50505050565b33600090815260d2602052604090205460ff16612903576040517f0da3c81800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61290d6000613385565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d2602052604090205460ff161561296d576040517fa187f37300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060cd544261297d9190614fcb565b905060405180606001604052808373ffffffffffffffffffffffffffffffffffffffff1681526020018267ffffffffffffffff1681526020016129c96129c260d15490565b6002613de0565b63ffffffff908116909152815160d48054602085015160409095015173ffffffffffffffffffffffffffffffffffffffff9093167fffffffff00000000000000000000000000000000000000000000000000000000909116177401000000000000000000000000000000000000000067ffffffffffffffff90951694909402939093177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c0100000000000000000000000000000000000000000000000000000000919092160217905560ce54612aa690612aa19042614fcb565b613453565b60405167ffffffffffffffff8216815273ffffffffffffffffffffffffffffffffffffffff8316907fc8ddece43d2b9acd5b55760836bc4a10fa43a243a009a2104bee292bb7a9e66f9060200160405180910390a25050565b612b076132ff565b60d354421015612b43576040517f6315bfbb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16612b62612672565b73ffffffffffffffffffffffffffffffffffffffff1603612baf576040517fa187f37300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d2602052604090205460ff1615612c0f576040517fa3da3fa600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116612c5c576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d2602052604090206002015415801590612cc7575060d05473ffffffffffffffffffffffffffffffffffffffff8216600090815260d26020526040902060020154612cc49190614fcb565b42105b15612cfe576040517fedf0644300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60cf54612d0b9042614fcb565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260d26020526040908190206002018390555190917f95e19500141d491c4c9063f86c635c2f9847b6b27d55b7afc81d646fee29c11b9161289591815260200190565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090735ff137d4b0fdcd49dca30c7cf57e578a026d2789906370a08231906024015b602060405180830381865afa158015612dd5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204a9190615056565b6040517f35567e1a00000000000000000000000000000000000000000000000000000000815230600482015260006024820181905290735ff137d4b0fdcd49dca30c7cf57e578a026d2789906335567e1a90604401612db8565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d260205260408120600201549003612eb4576040517ff3fff9d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d2602052604090205460ff16612f13576040517f0da3c81800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d26020526040902060020154421015612f75576040517f6cc73bf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d05473ffffffffffffffffffffffffffffffffffffffff8216600090815260d26020526040902060020154612fab9190614fcb565b421115612fe4576040517f1df84b1f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d1805460009190612ff890600190614fde565b8154811061300857613008614cff565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff9081169150821681146130d65773ffffffffffffffffffffffffffffffffffffffff8216600090815260d2602052604090206001015460d180548391908390811061307657613076614cff565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff948516179055918416815260d290915260409020600101555b60d18054806130e7576130e761506f565b6000828152602080822083017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905590920190925573ffffffffffffffffffffffffffffffffffffffff841680835260d2909152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168155600181018490556002018390555190917f0c92d12d8037dd6d77aed8d12addd54d5eb2a6801541a1bf87c9822e78eea42191a25050565b60d354421015613200576040517f6315bfbb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d2602052604090205460ff1615613260576040517fa187f37300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c5e81613e17565b33600090815260d2602052604090205460ff166132b2576040517f0da3c81800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d3544210156132ee576040517f6315bfbb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e8260ce5442612aa19190614fcb565b33613308612672565b73ffffffffffffffffffffffffffffffffffffffff1614611e82576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401611839565b8080156133b4575060d45474010000000000000000000000000000000000000000900467ffffffffffffffff16155b156133eb576040517f8a60636c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8015801561341c575060d45474010000000000000000000000000000000000000000900467ffffffffffffffff1615155b15610c5e576040517f8398fffc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405181151581527fe3f0ec9c4af57e69d5aeff78a5912ca25733e4458710bab2b55d0985e98aeb5e9060200160405180910390a160d355565b6000610b1161349a613eaf565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b60008060006134e48585613eb9565b915091506134f181613efe565b509392505050565b600065ffffffffffff821115613591576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203460448201527f38206269747300000000000000000000000000000000000000000000000000006064820152608401611839565b5090565b33735ff137d4b0fdcd49dca30c7cf57e578a026d2789148015906135ec57506135bc612672565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b15611e82576040517fbe8de9b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60d454604080517f9f7aca777caf11405930359f601a4db01fad1b2d79ef3f2f9e93c835e9feffa5602082015273ffffffffffffffffffffffffffffffffffffffff83169181019190915274010000000000000000000000000000000000000000820467ffffffffffffffff1660608201527c010000000000000000000000000000000000000000000000000000000090910463ffffffff166080820152600090819060a0016040516020818303038152906040528051906020012090506000613751858560008181106136f9576136f9614cff565b905060200281019061370b9190614ff1565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061374b925086915061348d9050565b906134d5565b90506137828173ffffffffffffffffffffffffffffffffffffffff16600090815260d2602052604090205460ff1690565b61379157600092505050610b11565b60015b8481101561388b5760006138058787848181106137b3576137b3614cff565b90506020028101906137c59190614ff1565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061374b925088915061348d9050565b90508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1611613847576000945050505050610b11565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260d2602052604090205460ff16613881576000945050505050610b11565b9150600101613794565b50600195945050505050565b609a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055610c5e816140b1565b33735ff137d4b0fdcd49dca30c7cf57e578a026d278914611e82576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f6163636f756e743a206e6f742066726f6d20456e747279506f696e74000000006044820152606401611839565b6040517fa61935310000000000000000000000000000000000000000000000000000000081526000908290735ff137d4b0fdcd49dca30c7cf57e578a026d27899063a61935319061399a90879060040161514b565b602060405180830381865afa1580156139b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139db9190615056565b14613a68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f43616c63756c6174656420757365724f704861736820646f65736e2774206d6160448201527f74636800000000000000000000000000000000000000000000000000000000006064820152608401611839565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c839052603c812090613ae6613aa9610140870187614ff1565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086939250506134d59050565b90508073ffffffffffffffffffffffffffffffffffffffff16613b07612672565b73ffffffffffffffffffffffffffffffffffffffff1603613b2d57600092505050610b11565b613b3e816104546060880188614ff1565b15613b915773ffffffffffffffffffffffffffffffffffffffff8116600090815260356020526040812054613b88919065ffffffffffff6601000000000000820481169116614128565b92505050610b11565b506001949350505050565b8015610c5e5760405160009033907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90849084818181858888f193505050503d8060008114613c07576040519150601f19603f3d011682016040523d82523d6000602084013e613c0c565b606091505b5050505050565b6000808573ffffffffffffffffffffffffffffffffffffffff16858585604051613c3e929190615286565b60006040518083038185875af1925050503d8060008114613c7b576040519150601f19603f3d011682016040523d82523d6000602084013e613c80565b606091505b509150915081613c9257805160208201fd5b505050505050565b600054610100900460ff16613d31576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401611839565b613d3b8282614160565b5050565b606060038054613d4e90615296565b80601f0160208091040260200160405190810160405280929190818152602001828054613d7a90615296565b8015613dc75780601f10613d9c57610100808354040283529160200191613dc7565b820191906000526020600020905b815481529060010190602001808311613daa57829003601f168201915b5050505050905090565b606060048054613d4e90615296565b60008215613e0e5781613df4600185614fde565b613dfe91906152e3565b613e09906001614fcb565b6113eb565b60009392505050565b613e1f6132ff565b609a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8316908117909155613e6a612672565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b600061204a61421f565b6000808251604103613eef5760208301516040840151606085015160001a613ee387828585614293565b94509450505050613ef7565b506000905060025b9250929050565b6000816004811115613f1257613f1261531e565b03613f1a5750565b6001816004811115613f2e57613f2e61531e565b03613f95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611839565b6002816004811115613fa957613fa961531e565b03614010576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611839565b60038160048111156140245761402461531e565b03610c5e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401611839565b6068805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600060d08265ffffffffffff16901b60a08465ffffffffffff16901b85614150576000614153565b60015b60ff161717949350505050565b600054610100900460ff166141f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401611839565b60036142038382615398565b5060046142108282615398565b50506000600181905560025550565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f61424a614382565b6142526143db565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156142ca5750600090506003614379565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561431e573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811661437257600060019250925050614379565b9150600090505b94509492505050565b60008061438d613d3f565b8051909150156143a4578051602090910120919050565b60015480156143b35792915050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4709250505090565b6000806143e6613dd1565b8051909150156143fd578051602090910120919050565b60025480156143b35792915050565b73ffffffffffffffffffffffffffffffffffffffff81168114610c5e57600080fd5b80356144398161440c565b919050565b60008083601f84011261445057600080fd5b50813567ffffffffffffffff81111561446857600080fd5b602083019150836020828501011115613ef757600080fd5b60008060008060008060008060c0898b03121561449c57600080fd5b88356144a78161440c565b975060208901356144b78161440c565b965060408901356144c78161440c565b955060608901359450608089013567ffffffffffffffff808211156144eb57600080fd5b6144f78c838d0161443e565b909650945060a08b013591508082111561451057600080fd5b5061451d8b828c0161443e565b999c989b5096995094979396929594505050565b60006020828403121561454357600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146113eb57600080fd5b6020808252825182820181905260009190848201906040850190845b818110156145c157835173ffffffffffffffffffffffffffffffffffffffff168352928401929184019160010161458f565b50909695505050505050565b6000602082840312156145df57600080fd5b81356113eb8161440c565b60008060008060006080868803121561460257600080fd5b853561460d8161440c565b9450602086013561461d8161440c565b935060408601359250606086013567ffffffffffffffff81111561464057600080fd5b61464c8882890161443e565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156146d3576146d361465d565b604052919050565b600082601f8301126146ec57600080fd5b813567ffffffffffffffff8111156147065761470661465d565b61473760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161468c565b81815284602083860101111561474c57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806040838503121561477c57600080fd5b82359150602083013567ffffffffffffffff81111561479a57600080fd5b6147a6858286016146db565b9150509250929050565b6000806000604084860312156147c557600080fd5b83356147d08161440c565b9250602084013567ffffffffffffffff8111156147ec57600080fd5b6147f88682870161443e565b9497909650939450505050565b803565ffffffffffff8116811461443957600080fd5b60008083601f84011261482d57600080fd5b50813567ffffffffffffffff81111561484557600080fd5b6020830191508360208260051b8501011115613ef757600080fd5b60008060008060008060a0878903121561487957600080fd5b86356148848161440c565b955061489260208801614805565b94506148a060408801614805565b93506148ae60608801614805565b9250608087013567ffffffffffffffff8111156148ca57600080fd5b6148d689828a0161481b565b979a9699509497509295939492505050565b600080602083850312156148fb57600080fd5b823567ffffffffffffffff81111561491257600080fd5b61491e8582860161481b565b90969095509350505050565b60008060006060848603121561493f57600080fd5b833567ffffffffffffffff81111561495657600080fd5b8401610160818703121561496957600080fd5b95602085013595506040909401359392505050565b6000806000806000806060878903121561499757600080fd5b863567ffffffffffffffff808211156149af57600080fd5b6149bb8a838b0161481b565b909850965060208901359150808211156149d457600080fd5b6149e08a838b0161481b565b909650945060408901359150808211156149f957600080fd5b506148d689828a0161481b565b600080600080600080600060e0888a031215614a2157600080fd5b8735614a2c8161440c565b96506020880135614a3c8161440c565b955060408801359450606088013593506080880135925060a0880135915060c0880135614a688161440c565b8091505092959891949750929550565b6000815180845260005b81811015614a9e57602081850181015186830182015201614a82565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e081840152614b1860e084018a614a78565b8381036040850152614b2a818a614a78565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825283870192509083019060005b81811015614b8957835183529284019291840191600101614b6d565b50909c9b505050505050505050505050565b60008060008060608587031215614bb157600080fd5b8435614bbc8161440c565b935060208501359250604085013567ffffffffffffffff811115614bdf57600080fd5b614beb8782880161443e565b95989497509550505050565b60008060008060008060008060a0898b031215614c1357600080fd5b8835614c1e8161440c565b97506020890135614c2e8161440c565b9650604089013567ffffffffffffffff80821115614c4b57600080fd5b614c578c838d0161481b565b909850965060608b0135915080821115614c7057600080fd5b614c7c8c838d0161481b565b909650945060808b013591508082111561451057600080fd5b60008060008060008060a08789031215614cae57600080fd5b8635614cb98161440c565b95506020870135614cc98161440c565b94506040870135935060608701359250608087013567ffffffffffffffff811115614cf357600080fd5b6148d689828a0161443e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008085851115614d3e57600080fd5b83861115614d4b57600080fd5b5050820193919092039150565b600080600060608486031215614d6d57600080fd5b8335614d788161440c565b925060208401359150604084013567ffffffffffffffff811115614d9b57600080fd5b614da7868287016146db565b9150509250925092565b600067ffffffffffffffff821115614dcb57614dcb61465d565b5060051b60200190565b600082601f830112614de657600080fd5b81356020614dfb614df683614db1565b61468c565b82815260059290921b84018101918181019086841115614e1a57600080fd5b8286015b84811015614e355780358352918301918301614e1e565b509695505050505050565b600082601f830112614e5157600080fd5b81356020614e61614df683614db1565b82815260059290921b84018101918181019086841115614e8057600080fd5b8286015b84811015614e3557803567ffffffffffffffff811115614ea45760008081fd5b614eb28986838b01016146db565b845250918301918301614e84565b600080600060608486031215614ed557600080fd5b833567ffffffffffffffff80821115614eed57600080fd5b818601915086601f830112614f0157600080fd5b81356020614f11614df683614db1565b82815260059290921b8401810191818101908a841115614f3057600080fd5b948201945b83861015614f57578535614f488161440c565b82529482019490820190614f35565b97505087013592505080821115614f6d57600080fd5b614f7987838801614dd5565b93506040860135915080821115614f8f57600080fd5b50614da786828701614e40565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610b1157610b11614f9c565b81810381811115610b1157610b11614f9c565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261502657600080fd5b83018035915067ffffffffffffffff82111561504157600080fd5b602001915036819003821315613ef757600080fd5b60006020828403121561506857600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126150d357600080fd5b830160208101925035905067ffffffffffffffff8111156150f357600080fd5b803603821315613ef757600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b602081526151796020820161515f8461442e565b73ffffffffffffffffffffffffffffffffffffffff169052565b602082013560408201526000615192604084018461509e565b6101608060608601526151aa61018086018385615102565b92506151b9606087018761509e565b92507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0808786030160808801526151f1858584615102565b9450608088013560a088015260a088013560c088015260c088013560e0880152610100935060e088013584880152610120915083880135828801526152388289018961509e565b94509150610140818887030181890152615253868685615102565b9550615261818a018a61509e565b95509250508087860301838801525061527b848483615102565b979650505050505050565b8183823760009101908152919050565b600181811c908216806152aa57607f821691505b602082108103610bef577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600082615319577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b601f82111561539357600081815260208120601f850160051c810160208610156153745750805b601f850160051c820191505b81811015613c9257828155600101615380565b505050565b815167ffffffffffffffff8111156153b2576153b261465d565b6153c6816153c08454615296565b8461534d565b602080601f83116001811461541957600084156153e35750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613c92565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561546657888601518255948401946001909101908401615447565b50858210156154a257878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b0190555056fea26469706673582212202ba50605d173b5aa6cd0c8f60b53b0bea0ba8997b590e4160bf1c1a24e9f35c864736f6c63430008130033 --private-key $PK_PAYMASTER_OWNER_TESTNET \ No newline at end of file diff --git a/script/RawVerifyAcc-0.5.0.sh b/script/RawVerifyAcc-0.5.0.sh new file mode 100644 index 0000000..69dc0cc --- /dev/null +++ b/script/RawVerifyAcc-0.5.0.sh @@ -0,0 +1 @@ +forge verify-contract --chain-id 5 --num-of-optimizations 1000000 --watch --compiler-version v0.8.19+commit.7dd6d404 0x36604309934A2Fc92C3445Cf4566b23b5b4BbAad contracts/core/managed/ManagedOpenfortAccount.sol:ManagedOpenfortAccount -e $ETHERSCAN_API_KEY --verifier etherscan diff --git a/script/RawVerifyFact-0.5.0.sh b/script/RawVerifyFact-0.5.0.sh new file mode 100644 index 0000000..a247b6b --- /dev/null +++ b/script/RawVerifyFact-0.5.0.sh @@ -0,0 +1 @@ +forge verify-contract --chain-id 5 --num-of-optimizations 1000000 --watch --compiler-version v0.8.19+commit.7dd6d404 0x44A7d7B291834442EE3703bD8bB7f91eD6F2577E contracts/core/managed/ManagedOpenfortFactory.sol:ManagedOpenfortFactory -e $ETHERSCAN_API_KEY --verifier etherscan --constructor-args $(cast abi-encode "constructor(address,address,address,uint256,uint256,uint256,uint256,address)" 0x9590Ed0C18190a310f4e93CAccc4CC17270bED40 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 0x36604309934A2Fc92C3445Cf4566b23b5b4BbAad 172800 129600 43200 432000 0x9590Ed0C18190a310f4e93CAccc4CC17270bED40) From 709a684f3542af40d717537d8ac237448552b14f Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 29 Nov 2023 15:56:38 +0100 Subject: [PATCH 56/83] DeployMock cleaned --- script/deployMock.s.sol | 14 +++++++------- .../core/managed/ManagedOpenfortAccountTest.t.sol | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/script/deployMock.s.sol b/script/deployMock.s.sol index 2ef75fc..8e7c638 100644 --- a/script/deployMock.s.sol +++ b/script/deployMock.s.sol @@ -1,22 +1,22 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19; -import {Script, console} from "forge-std/Script.sol"; -import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; -import {UpgradeableOpenfortFactory} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; +import {Script} from "forge-std/Script.sol"; import {MockERC20} from "../contracts/mock/MockERC20.sol"; +import {MockERC721} from "../contracts/mock/MockERC721.sol"; contract DeployMock is Script { - uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC"), 0); - // uint256 internal deployPrivKey = vm.envUint("PK"); + uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); address internal deployAddress = vm.addr(deployPrivKey); - IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); function run() public { + bytes32 versionSalt = bytes32(0x0); vm.startBroadcast(deployPrivKey); - MockERC20 mockERC20 = new MockERC20(); + MockERC20 mockERC20 = new MockERC20{salt: versionSalt}(); (mockERC20); + MockERC721 mockERC721 = new MockERC721{salt: versionSalt}(); + (mockERC721); vm.stopBroadcast(); } diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index becd60e..abc4f5f 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -33,6 +33,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * - testCounter is the counter used to test userOps */ function setUp() public { + versionSalt = bytes32(0x0); // Setup and fund signers (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); vm.deal(factoryAdmin, 100 ether); From cadb02516d3db84a8082a7de9180b12076d81fa8 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 29 Nov 2023 17:19:06 +0100 Subject: [PATCH 57/83] ERC6551 working non-upgradeable --- .../core/erc6551/ERC6551OpenfortAccount.sol | 14 +- .../core/erc6551/ERC6551OpenfortProxy.sol | 18 -- .../erc6551/EIP6551OpenfortAccountTest.t.sol | 295 ------------------ .../erc6551/ERC6551OpenfortAccountTest.sol | 274 ++++++++++++++++ 4 files changed, 281 insertions(+), 320 deletions(-) delete mode 100644 contracts/core/erc6551/ERC6551OpenfortProxy.sol delete mode 100644 test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol create mode 100644 test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol diff --git a/contracts/core/erc6551/ERC6551OpenfortAccount.sol b/contracts/core/erc6551/ERC6551OpenfortAccount.sol index 21ac3ad..e936769 100644 --- a/contracts/core/erc6551/ERC6551OpenfortAccount.sol +++ b/contracts/core/erc6551/ERC6551OpenfortAccount.sol @@ -21,25 +21,24 @@ import {BaseOpenfortAccount, IEntryPoint, ECDSAUpgradeable} from "../base/BaseOp contract ERC6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC6551Executable { using ECDSAUpgradeable for bytes32; - address internal entrypointContract; - // bytes4(keccak256("execute(address,uint256,bytes,uint8)") bytes4 internal constant EXECUTE_ERC6551_SELECTOR = 0x51945447; + address constant DEFAULT_ENTRYPOINT = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; + address internal entrypointContract; uint256 public state; error OperationNotAllowed(); receive() external payable override(BaseOpenfortAccount, IERC6551Account) {} + constructor() {} + /* * @notice Initialize the smart contract wallet. */ - function initialize(address _entrypoint) public initializer { - _requireFromOwner(); - if (_entrypoint == address(0)) revert ZeroAddressNotAllowed(); - emit EntryPointUpdated(entrypointContract, _entrypoint); - entrypointContract = _entrypoint; + function initialize() public initializer { + entrypointContract = DEFAULT_ENTRYPOINT; __EIP712_init("Openfort", "0.5"); state = 1; } @@ -82,6 +81,7 @@ contract ERC6551OpenfortAccount is BaseOpenfortAccount, IERC6551Account, IERC655 returns (bytes memory _result) { if (_operation != 0) revert OperationNotAllowed(); + _requireFromEntryPointOrOwner(); ++state; bool success; (success, _result) = _target.call{value: _value}(_data); diff --git a/contracts/core/erc6551/ERC6551OpenfortProxy.sol b/contracts/core/erc6551/ERC6551OpenfortProxy.sol deleted file mode 100644 index 6223583..0000000 --- a/contracts/core/erc6551/ERC6551OpenfortProxy.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - -/** - * @title ERC6551OpenfortProxy (Non-upgradeable) - * @notice Contract to create the ERC6551 proxies - * It inherits from: - * - ERC1967Proxy - */ -contract ERC6551OpenfortProxy is ERC1967Proxy { - constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) {} - - function implementation() external view returns (address) { - return _implementation(); - } -} diff --git a/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol b/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol deleted file mode 100644 index 46d860f..0000000 --- a/test/foundry/core/erc6551/EIP6551OpenfortAccountTest.t.sol +++ /dev/null @@ -1,295 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.19; - -// import {console} from "lib/forge-std/src/Test.sol"; -// import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -// import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; -// import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -// import {MockERC721} from "contracts/mock/MockERC721.sol"; -// import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; -// import {ERC6551OpenfortProxy} from "contracts/core/erc6551/ERC6551OpenfortProxy.sol"; -// import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; -// import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; - -// contract ERC6551OpenfortAccountTest is OpenfortBaseTest { -// using ECDSA for bytes32; - -// ERC6551Registry public erc6551Registry; -// ERC6551OpenfortAccount public erc6551OpenfortAccount; -// ERC6551OpenfortAccount public erc6551OpenfortAccountImpl; -// ERC6551OpenfortProxy public erc6551OpenfortAccountProxy; -// MockERC721 public mockERC721; - -// /** -// * @notice Initialize the StaticOpenfortAccount testing contract. -// * Scenario: -// * - factoryAdmin is the deployer (and owner) of the mockNFT -// * - accountAdmin is the account used to deploy new static accounts -// * - entryPoint is the singleton EntryPoint -// * - testCounter is the counter used to test userOps -// */ -// function setUp() public { -// versionSalt = bytes32(0x0); -// // Setup and fund signers -// (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); -// vm.deal(factoryAdmin, 100 ether); -// (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); -// vm.deal(accountAdmin, 100 ether); - -// uint256 chainId; -// assembly { -// chainId := chainid() -// } -// console.log("ChainId:", chainId); - -// vm.startPrank(factoryAdmin); - -// // If we are in a fork -// if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { -// entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); -// } -// // If not a fork, deploy entryPoint (at the correct address) -// else { -// EntryPoint entryPoint_aux = new EntryPoint(); -// bytes memory code = address(entryPoint_aux).code; -// address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); -// vm.etch(targetAddr, code); -// entryPoint = EntryPoint(payable(targetAddr)); -// } - -// // If we are in a fork -// if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { -// erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); -// console.log("Using ERC6551 registry from a fork"); -// } -// // If not a fork, deploy ERC6551 registry (at the correct address) -// else { -// ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); -// bytes memory code = address(ERC6551Registry_aux).code; -// address targetAddr = address(vm.envAddress("ERC6551_REGISTRY_ADDRESS")); -// vm.etch(targetAddr, code); -// erc6551Registry = ERC6551Registry(payable(targetAddr)); -// } - -// // deploy a new MockERC721 collection -// mockERC721 = new MockERC721{salt: versionSalt}(); - -// erc6551OpenfortAccountImpl = new ERC6551OpenfortAccount{salt: versionSalt}(); - -// erc6551OpenfortAccountProxy = -// new ERC6551OpenfortProxy{salt: versionSalt}(address(erc6551OpenfortAccountImpl), ""); - -// erc6551OpenfortAccountProxy.implementation(); -// erc6551OpenfortAccountProxy.implementation(); -// erc6551OpenfortAccountProxy.implementation(); -// erc6551OpenfortAccountProxy.implementation(); - -// accountAddress = erc6551Registry.createAccount( -// address(erc6551OpenfortAccountProxy), versionSalt, chainId, address(mockERC721), 1 -// ); - -// vm.stopPrank(); - -// erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(accountAddress)); -// vm.expectRevert(); -// erc6551OpenfortAccount.initialize(address(entryPoint)); -// console.log("State: ", erc6551OpenfortAccount.state()); - -// vm.expectRevert(OpenfortErrorsAndEvents.NotOwner.selector); -// erc6551OpenfortAccount.initialize(address(entryPoint)); - -// vm.prank(factoryAdmin); -// mockERC721.mint(accountAdmin, 1); - -// vm.prank(accountAdmin); -// erc6551OpenfortAccount.initialize(address(entryPoint)); -// } - -// /* -// * Test get implementation from proxy. -// */ -// function testGetImplementation() public view { -// ERC6551OpenfortProxy p = ERC6551OpenfortProxy(payable(accountAddress)); -// p.implementation(); -// } - -// /* -// * Test reinitialize. It should fail. -// */ -// function testFailReinitialize() public { -// vm.prank(accountAdmin); -// erc6551OpenfortAccount.initialize(address(entryPoint)); -// } - -// /* -// * Test initialize implementation. It should fail. -// */ -// function testFailInitializeImplementation() public { -// erc6551OpenfortAccountImpl.initialize(address(entryPoint)); -// } - -// /* -// * Test deploy. Regular, no userOps. -// */ -// function testERC6551Deploy() public { -// address deployedAccount = -// erc6551Registry.createAccount(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); - -// assertTrue(deployedAccount != address(0)); - -// address predictedAccount = -// erc6551Registry.account(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); - -// assertEq(predictedAccount, deployedAccount); -// } - -// /* -// * Check implementation has not been initialized. -// * EntryPoint address should be 0. Should pass. -// */ -// function testImplementationNoEntryPointAddr() public { -// IEntryPoint e = erc6551OpenfortAccountImpl.entryPoint(); -// assertEq(address(e), address(0)); -// } - -// /* -// * Create a 2nd account using the same technique than in setup with a new salt (2). -// */ -// function testCreate2ndAcc() public { -// uint256 chainId; -// assembly { -// chainId := chainid() -// } -// address accountAddress2 = erc6551Registry.createAccount( -// address(erc6551OpenfortAccountImpl), bytes32(0), chainId, address(mockERC721), 1 -// ); - -// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); -// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); -// assertEq(address(e), address(entryPoint)); -// assertNotEq(address(e), accountAddress2); -// } - -// /* -// * Create a new account using createAccount() and the initializer. -// */ -// function testCreateAccInitializer() public { -// uint256 chainId; -// assembly { -// chainId := chainid() -// } -// address accountAddress2 = erc6551Registry.createAccount( -// address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 -// ); -// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); -// IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); -// assertEq(address(e), address(entryPoint)); -// } - -// /* -// * Create a new account using createAccount() and the initializer. -// * Test initialize again should fail. -// */ -// function testFailCreateAccInitializerNoReinit() public { -// uint256 chainId; -// assembly { -// chainId := chainid() -// } -// address accountAddress2 = erc6551Registry.createAccount( -// address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 -// ); - -// ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); -// erc6551OpenfortAccount2.initialize(address(entryPoint)); -// } - -// /* -// * Test getDeposit() function. -// * First ERC4337 function called by this ERC6551-compatible account. -// */ -// function testGetDeposit() public { -// uint256 deposit; -// deposit = erc6551OpenfortAccount.getDeposit(); -// assertEq(deposit, 0); - -// // We can add deposit by directly calling the EntryPoint -// entryPoint.depositTo{value: 1 ether}(address(erc6551OpenfortAccount)); -// deposit = erc6551OpenfortAccount.getDeposit(); -// assertEq(deposit, 1 ether); - -// // We can ALSO add deposit by calling the EntryPoint depositTo() function -// vm.prank(accountAdmin); -// erc6551OpenfortAccount.execute{value: 1 ether}( -// address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) -// ); -// deposit = erc6551OpenfortAccount.getDeposit(); -// assertEq(deposit, 2 ether); -// } - -// /* -// * Test owner() function. -// * Check that the owner of the erc6551 account is the owner of the NFT -// */ -// function testOwner() public { -// assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); -// assertEq(erc6551OpenfortAccount.owner(), accountAdmin); -// } - -// /* -// * Test owner() function. -// * Check that the owner of the erc6551 account is the owner of the NFT -// */ -// function testNotOwner() public { -// // Burning the NFT -// vm.prank(accountAdmin); -// mockERC721.transferFrom(accountAdmin, address(1), 1); - -// assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); -// assertNotEq(erc6551OpenfortAccount.owner(), accountAdmin); -// assertEq(erc6551OpenfortAccount.owner(), address(1)); -// } - -// /* -// * Create an account by directly calling the registry. -// */ -// function testCreateAccountWithNonceViaRegistry() public { -// uint256 chainId; -// assembly { -// chainId := chainid() -// } - -// // Get the counterfactual address -// vm.prank(factoryAdmin); -// address accountAddress2 = -// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); - -// // Expect that we will see an event containing the account and admin -// // vm.expectEmit(true, true, false, true); -// // emit IERC6551Registry.ERC6551AccountCreated( -// // accountAddress2, address(erc6551OpenfortAccount), chainId, address(mockERC721), 1, 2 -// // ); - -// // Deploy a static account to the counterfactual address -// vm.prank(factoryAdmin); -// erc6551Registry.createAccount(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1); - -// // Make sure the counterfactual address has not been altered -// vm.prank(factoryAdmin); -// assertEq( -// accountAddress2, -// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) -// ); -// // assertNotEq( -// // accountAddress2, -// // erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(mockERC721), 1) -// // ); -// assertNotEq( -// accountAddress2, -// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId + 1, address(mockERC721), 1) -// ); -// assertNotEq( -// accountAddress2, -// erc6551Registry.account(address(erc6551OpenfortAccount), versionSalt, chainId, address(0), 1) -// ); -// } -// } diff --git a/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol new file mode 100644 index 0000000..985769c --- /dev/null +++ b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {console} from "lib/forge-std/src/Test.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; +import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +import {TestCounter} from "account-abstraction/test/TestCounter.sol"; +import {MockERC721} from "contracts/mock/MockERC721.sol"; +import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; +import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; +import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; + +contract ERC6551OpenfortAccountTest is OpenfortBaseTest { + using ECDSA for bytes32; + + ERC6551Registry public erc6551Registry; + ERC6551OpenfortAccount public erc6551OpenfortAccountImpl; + ERC6551OpenfortAccount public erc6551OpenfortAccount; + + /** + * @notice Initialize the StaticOpenfortAccount testing contract. + * Scenario: + * - factoryAdmin is the deployer (and owner) of the mockNFT + * - accountAdmin is the account used to deploy new static accounts + * - entryPoint is the singleton EntryPoint + * - testCounter is the counter used to test userOps + */ + function setUp() public { + versionSalt = bytes32(0x0); + // Setup and fund signers + (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); + vm.deal(factoryAdmin, 100 ether); + (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); + vm.deal(accountAdmin, 100 ether); + + uint256 chainId; + assembly { + chainId := chainid() + } + console.log("ChainId:", chainId); + + vm.startPrank(factoryAdmin); + + // If we are in a fork + if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { + entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); + } + // If not a fork, deploy entryPoint (at the correct address) + else { + EntryPoint entryPoint_aux = new EntryPoint(); + bytes memory code = address(entryPoint_aux).code; + address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); + vm.etch(targetAddr, code); + entryPoint = EntryPoint(payable(targetAddr)); + } + + // If we are in a fork + if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { + erc6551Registry = ERC6551Registry(payable(vm.envAddress("ERC6551_REGISTRY_ADDRESS"))); + console.log("Using ERC6551 registry from a fork"); + } + // If not a fork, deploy ERC6551 registry (at the correct address) + else { + ERC6551Registry ERC6551Registry_aux = new ERC6551Registry(); + bytes memory code = address(ERC6551Registry_aux).code; + address targetAddr = address(vm.envAddress("ERC6551_REGISTRY_ADDRESS")); + vm.etch(targetAddr, code); + erc6551Registry = ERC6551Registry(payable(targetAddr)); + } + + // deploy a new MockERC721 collection + mockERC721 = new MockERC721{salt: versionSalt}(); + + erc6551OpenfortAccountImpl = new ERC6551OpenfortAccount{salt: versionSalt}(); + + accountAddress = erc6551Registry.createAccount( + address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 + ); + + mockERC721.mint(accountAdmin, 1); + + // deploy a new TestCounter + testCounter = new TestCounter{salt: versionSalt}(); + + vm.stopPrank(); + + erc6551OpenfortAccount = ERC6551OpenfortAccount(payable(accountAddress)); + } + + /* + * Test deploy. Regular, no userOps. + */ + function testERC6551Deploy() public { + address deployedAccount = + erc6551Registry.createAccount(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); + + assertTrue(deployedAccount != address(0)); + + address predictedAccount = + erc6551Registry.account(address(erc6551OpenfortAccountImpl), 0, block.chainid, address(0), 0); + + assertEq(predictedAccount, deployedAccount); + } + + /* + * Check implementation has not been initialized. + * EntryPoint address should be 0 always in the implementation address. Should pass. + */ + function testImplementationNoEntryPointAddr() public { + assertEq(address(erc6551OpenfortAccountImpl.entryPoint()), address(0)); + vm.expectRevert("Initializable: contract is already initialized"); + erc6551OpenfortAccountImpl.initialize(); + assertEq(address(erc6551OpenfortAccountImpl.entryPoint()), address(0)); + } + + /* + * Create a 2nd account using the same technique than in setup with a new salt (2). + */ + function testCreate2ndAcc() public { + uint256 chainId; + assembly { + chainId := chainid() + } + address accountAddress2 = erc6551Registry.createAccount( + address(erc6551OpenfortAccountImpl), bytes32(0), chainId, address(mockERC721), 1 + ); + + ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); + erc6551OpenfortAccount2.initialize(); + IEntryPoint e = erc6551OpenfortAccount2.entryPoint(); + assertEq(address(e), address(entryPoint)); + assertNotEq(address(e), accountAddress2); + } + + /* + * Create a new account using createAccount() and the initializer. + */ + function testCreateAccInitializer() public { + uint256 chainId; + assembly { + chainId := chainid() + } + address accountAddress2 = erc6551Registry.createAccount( + address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 + ); + ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); + assertEq(address(erc6551OpenfortAccount2.entryPoint()), address(0)); + erc6551OpenfortAccount2.initialize(); + assertEq(address(erc6551OpenfortAccount2.entryPoint()), address(entryPoint)); + } + + /* + * Create a new account using createAccount() and the initializer. + * Test initialize again should fail. + */ + function testFailCreateAccInitializerNoReinit() public { + uint256 chainId; + assembly { + chainId := chainid() + } + address accountAddress2 = erc6551Registry.createAccount( + address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 + ); + + ERC6551OpenfortAccount erc6551OpenfortAccount2 = ERC6551OpenfortAccount(payable(accountAddress2)); + erc6551OpenfortAccount2.initialize(); + erc6551OpenfortAccount2.initialize(); + } + + /* + * Test getDeposit() function. + * First ERC4337 function called by this ERC6551-compatible account. + */ + function testGetDeposit() public { + erc6551OpenfortAccount.initialize(); + uint256 deposit = erc6551OpenfortAccount.getDeposit(); + assertEq(deposit, 0); + + // We can add deposit by directly calling the EntryPoint + entryPoint.depositTo{value: 1 ether}(address(erc6551OpenfortAccount)); + deposit = erc6551OpenfortAccount.getDeposit(); + assertEq(deposit, 1 ether); + + // We can ALSO add deposit by calling the EntryPoint depositTo() function + vm.prank(accountAdmin); + erc6551OpenfortAccount.execute{value: 1 ether}( + address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) + ); + deposit = erc6551OpenfortAccount.getDeposit(); + assertEq(deposit, 2 ether); + } + + /* + * Test owner() function. + * Check that the owner of the erc6551 account is the owner of the NFT + * Notice, no need to initialize yet. + */ + function testOwner() public { + assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); + assertEq(erc6551OpenfortAccount.owner(), accountAdmin); + } + + /* + * Test owner() function. + * Check that the owner of the erc6551 account is the owner of the NFT + */ + function testNotOwner() public { + // Burning the NFT + vm.prank(accountAdmin); + mockERC721.transferFrom(accountAdmin, address(1), 1); + + assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); + assertNotEq(erc6551OpenfortAccount.owner(), accountAdmin); + assertEq(erc6551OpenfortAccount.owner(), address(1)); + } + + function testERC6551ExecuteTransferEth() public { + erc6551OpenfortAccount.initialize(); + vm.deal(accountAddress, 1 ether); + + vm.prank(accountAdmin); + erc6551OpenfortAccount.execute(payable(vm.addr(2)), 0.5 ether, "", 0); + + assertEq(accountAddress.balance, 0.5 ether); + assertEq(vm.addr(2).balance, 0.5 ether); + assertEq(erc6551OpenfortAccount.state(), 2); + + vm.prank(accountAdmin); + erc6551OpenfortAccount.execute(payable(vm.addr(2)), 0.5 ether, ""); + + assertEq(accountAddress.balance, 0 ether); + assertEq(vm.addr(2).balance, 1 ether); + assertEq(erc6551OpenfortAccount.state(), 3); + } + + /* + * Create an account using the factory and make it call count() directly. + */ + function testIncrementCounterDirect() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + // Make the admin of the upgradeable account wallet (deployer) call "count" + vm.prank(accountAdmin); + erc6551OpenfortAccount.execute(address(testCounter), 0, abi.encodeWithSignature("count()")); + + // Verify that the counter has increased + assertEq(testCounter.counters(accountAddress), 1); + } + + /* + * Create an account by directly calling the factory and make it call count() + * using the execute() function using the EntryPoint (userOp). Leveraging ERC-4337. + */ + function testIncrementCounterViaEntrypoint() public { + // If we want to use userOps, we need to initialize + erc6551OpenfortAccount.initialize(); + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + UserOperation[] memory userOp = _setupUserOpExecute( + accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + ); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + // Verify that the counter has increased + assertEq(testCounter.counters(accountAddress), 1); + } +} From 13ea266fdd5b67be1f0bdc48682916b07f617307 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 29 Nov 2023 18:51:29 +0100 Subject: [PATCH 58/83] Improving test coverage --- .../erc6551/ERC6551OpenfortAccountTest.sol | 131 ++++++++++++++++++ .../UpgradeableOpenfortAccountTest.t.sol | 47 ++++++- 2 files changed, 174 insertions(+), 4 deletions(-) diff --git a/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol index 985769c..51d7e43 100644 --- a/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol +++ b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol @@ -3,10 +3,15 @@ pragma solidity =0.8.19; import {console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC721} from "contracts/mock/MockERC721.sol"; +import {IERC6551Account} from "erc6551/src/interfaces/IERC6551Account.sol"; +import {IERC6551Executable} from "erc6551/src/interfaces/IERC6551Executable.sol"; import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; @@ -199,6 +204,48 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { function testOwner() public { assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); assertEq(erc6551OpenfortAccount.owner(), accountAdmin); + + vm.chainId(9999999); + assertEq(erc6551OpenfortAccount.owner(), address(0)); + } + + /* + * Test token() function. + * Notice, no need to initialize yet. + */ + function testToken() public { + uint256 _chainId; + assembly { + _chainId := chainid() + } + (uint256 chainId, address tokenContract, uint256 tokenId) = erc6551OpenfortAccount.token(); + assertEq(chainId, _chainId); + assertEq(tokenContract, address(mockERC721)); + assertEq(tokenId, 1); + + erc6551OpenfortAccount.initialize(); + + (uint256 chainId2, address tokenContract2, uint256 tokenId2) = erc6551OpenfortAccount.token(); + assertEq(chainId2, _chainId); + assertEq(tokenContract2, address(mockERC721)); + assertEq(tokenId2, 1); + } + + /* + * Test isValidSigner() function. + * Notice, no need to initialize yet. + */ + function testIsValidSigner() public { + bytes4 isValid = erc6551OpenfortAccount.isValidSigner(vm.addr(2), ""); + assertEq(isValid, 0); + + isValid = erc6551OpenfortAccount.isValidSigner(accountAdmin, ""); + assertEq(isValid, erc6551OpenfortAccount.isValidSigner.selector); + + erc6551OpenfortAccount.initialize(); + + isValid = erc6551OpenfortAccount.isValidSigner(accountAdmin, ""); + assertEq(isValid, erc6551OpenfortAccount.isValidSigner.selector); } /* @@ -219,6 +266,10 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { erc6551OpenfortAccount.initialize(); vm.deal(accountAddress, 1 ether); + vm.prank(accountAdmin); + vm.expectRevert(ERC6551OpenfortAccount.OperationNotAllowed.selector); + erc6551OpenfortAccount.execute(payable(vm.addr(2)), 0.5 ether, "", 1); + vm.prank(accountAdmin); erc6551OpenfortAccount.execute(payable(vm.addr(2)), 0.5 ether, "", 0); @@ -234,6 +285,31 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { assertEq(erc6551OpenfortAccount.state(), 3); } + function testERC6551ExecuteTransferEthBatch() public { + erc6551OpenfortAccount.initialize(); + vm.deal(accountAddress, 1 ether); + + uint256 count = 2; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + targets[0] = payable(vm.addr(2)); + targets[1] = payable(vm.addr(3)); + values[0] = 0.25 ether; + values[1] = 0.25 ether; + callData[0] = ""; + callData[1] = ""; + + vm.prank(accountAdmin); + erc6551OpenfortAccount.executeBatch(targets, values, callData); + + assertEq(accountAddress.balance, 0.5 ether); + assertEq(vm.addr(2).balance, 0.25 ether); + assertEq(vm.addr(3).balance, 0.25 ether); + assertEq(erc6551OpenfortAccount.state(), 3); + } + /* * Create an account using the factory and make it call count() directly. */ @@ -271,4 +347,59 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { // Verify that the counter has increased assertEq(testCounter.counters(accountAddress), 1); } + + function testSupportsInterface() public { + assertTrue(erc6551OpenfortAccount.supportsInterface(type(IERC6551Account).interfaceId)); + assertTrue(erc6551OpenfortAccount.supportsInterface(type(IERC6551Executable).interfaceId)); + assertTrue(erc6551OpenfortAccount.supportsInterface(type(IERC721Receiver).interfaceId)); + assertTrue(erc6551OpenfortAccount.supportsInterface(type(IERC1155Receiver).interfaceId)); + assertTrue(erc6551OpenfortAccount.supportsInterface(type(IERC165).interfaceId)); + assertFalse(erc6551OpenfortAccount.supportsInterface(bytes4(0x0000))); + } + + function testUpdateEntryPoint() public { + vm.expectRevert(OpenfortErrorsAndEvents.NotOwner.selector); + erc6551OpenfortAccount.updateEntryPoint(vm.addr(2)); + + vm.prank(accountAdmin); + vm.expectRevert(OpenfortErrorsAndEvents.ZeroAddressNotAllowed.selector); + erc6551OpenfortAccount.updateEntryPoint(address(0)); + + vm.prank(accountAdmin); + erc6551OpenfortAccount.updateEntryPoint(vm.addr(2)); + + // Weird behaviour, but not necessarily bad; if the user changes the EntryPoint address + // before initializing, the EntryPoint is set to the default + vm.prank(accountAdmin); + erc6551OpenfortAccount.initialize(); + + vm.prank(accountAdmin); + erc6551OpenfortAccount.updateEntryPoint(vm.addr(2)); + } + + /* + * Test onERC721Received() + */ + function testSafeTransferFrom() public { + vm.prank(accountAdmin); + vm.expectRevert("Cannot own yourself"); + mockERC721.safeTransferFrom(accountAdmin, address(erc6551OpenfortAccount), 1); + + uint256 _chainId; + assembly { + _chainId := chainid() + } + address accountAddress2 = erc6551Registry.createAccount( + address(erc6551OpenfortAccountImpl), versionSalt, _chainId, address(mockERC721), 2 + ); + + // Try with token ID 2 not minted yet + vm.prank(accountAdmin); + vm.expectRevert("ERC721: invalid token ID"); + mockERC721.safeTransferFrom(accountAdmin, accountAddress2, 1); + + mockERC721.mint(accountAdmin, 2); + vm.prank(accountAdmin); + mockERC721.safeTransferFrom(accountAdmin, accountAddress2, 1); + } } diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 71d3b72..b8fe035 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -4,6 +4,10 @@ pragma solidity =0.8.19; import {console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; @@ -15,10 +19,6 @@ import {UpgradeableOpenfortProxy} from "contracts/core/upgradeable/UpgradeableOp import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; import {OpenfortBaseTest, MockERC20, MockERC721, MockERC1155} from "../OpenfortBaseTest.t.sol"; import {SimpleNFT} from "contracts/mock/SimpleNFT.sol"; -import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; -import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; -import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; @@ -2837,6 +2837,33 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(mockERC1155.balanceOf(accountAddress2, 1), 1); } + /* + * Test an account with mockERC1155 sending it to another. + */ + function testTransferERC1155BetweenAccountsBatch() public { + assertEq(mockERC1155.balanceOf(accountAddress, 1), 0); + + address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", true); + + mockERC1155.mint(accountAdmin, 1, 2); + + assertEq(mockERC1155.balanceOf(accountAdmin, 1), 2); + assertEq(mockERC1155.balanceOf(accountAddress2, 1), 0); + + uint256[] memory ids = new uint256[](2); + ids[0] = 1; + ids[1] = 1; + uint256[] memory amounts = new uint256[](2); + amounts[0] = 1; + amounts[1] = 1; + + vm.prank(accountAdmin); + mockERC1155.safeBatchTransferFrom(accountAdmin, accountAddress2, ids, amounts, ""); + + assertEq(mockERC1155.balanceOf(accountAdmin, 1), 0); + assertEq(mockERC1155.balanceOf(accountAddress2, 1), 2); + } + /* * Test for coverage purposes. * SimpleNFT is an NFT contract used by Openfort in some internal tests @@ -2855,4 +2882,16 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertTrue(account.supportsInterface(type(IERC165).interfaceId)); assertFalse(account.supportsInterface(bytes4(0x0000))); } + + function testUpdateInitialGuardian() public { + vm.expectRevert(); + openfortFactory.updateInitialGuardian(vm.addr(2)); + + vm.prank(factoryAdmin); + vm.expectRevert(); + openfortFactory.updateInitialGuardian(address(0)); + + vm.prank(factoryAdmin); + openfortFactory.updateInitialGuardian(address(accountAdmin)); + } } From 83e89b37698ff59f092000fe557a64e57ac73fd6 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 1 Dec 2023 10:09:23 +0100 Subject: [PATCH 59/83] Adapt checkDeposits testnets --- .env.example | 3 ++- script/OpenfortForksConfig.s.sol | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index c2f107b..8e08b04 100644 --- a/.env.example +++ b/.env.example @@ -21,7 +21,8 @@ AVALANCHE_OPENFORT_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" GNOSIS_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" GNOSIS_CHIADO_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" BASE_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" -BASE_TEST_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" +BASE_GOERLI_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" +BASE_SEPOLIA_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" OASYS_MAINNET_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" OASYS_TEST_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" KLAYTN_MAINNET_RPC="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" diff --git a/script/OpenfortForksConfig.s.sol b/script/OpenfortForksConfig.s.sol index 2a7f163..c15e719 100644 --- a/script/OpenfortForksConfig.s.sol +++ b/script/OpenfortForksConfig.s.sol @@ -105,7 +105,7 @@ abstract contract OpenfortForksConfig is Script { paymasterAddresses[uint256(Forks.ArbitrumTestFork)] = openfortPaymasterV2Testnet; // Fork: Base Goerli testnet - vm.createFork(vm.envString("GOERLI_BASE_RPC")); + vm.createFork(vm.envString("BASE_GOERLI_RPC")); paymasterOwnerAddresses[uint256(Forks.BaseGoerliFork)] = openfortPaymasterOwnerTestnet; paymasterAddresses[uint256(Forks.BaseGoerliFork)] = openfortPaymasterV2Testnet; From 2d432504b64ccac26dabd980d4bfc1d8935d9b20 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 1 Dec 2023 12:28:54 +0100 Subject: [PATCH 60/83] Migrating checkDeposit from Goerli to Sepolia --- script/OpenfortForksConfig.s.sol | 14 +++++++------- script/checkDeposits.s.sol | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/script/OpenfortForksConfig.s.sol b/script/OpenfortForksConfig.s.sol index c15e719..c513f58 100644 --- a/script/OpenfortForksConfig.s.sol +++ b/script/OpenfortForksConfig.s.sol @@ -21,7 +21,7 @@ abstract contract OpenfortForksConfig is Script { FujiFork, BscTestFork, ArbitrumTestFork, - BaseGoerliFork, + BaseTestFork, BeamTestnetFork, ChiadoFork, LineaTestnetFork, @@ -99,15 +99,15 @@ abstract contract OpenfortForksConfig is Script { paymasterOwnerAddresses[uint256(Forks.BscTestFork)] = openfortPaymasterOwnerTestnet; paymasterAddresses[uint256(Forks.BscTestFork)] = openfortPaymasterV2Testnet; - // Fork: Arbitrum Goerli testnet - vm.createFork(vm.envString("ARBITRUM_GOERLI_RPC")); + // Fork: Arbitrum Sepolia testnet + vm.createFork(vm.envString("ARBITRUM_SEPOLIA_RPC")); paymasterOwnerAddresses[uint256(Forks.ArbitrumTestFork)] = openfortPaymasterOwnerTestnet; paymasterAddresses[uint256(Forks.ArbitrumTestFork)] = openfortPaymasterV2Testnet; - // Fork: Base Goerli testnet - vm.createFork(vm.envString("BASE_GOERLI_RPC")); - paymasterOwnerAddresses[uint256(Forks.BaseGoerliFork)] = openfortPaymasterOwnerTestnet; - paymasterAddresses[uint256(Forks.BaseGoerliFork)] = openfortPaymasterV2Testnet; + // Fork: Base Sepolia testnet + vm.createFork(vm.envString("BASE_SEPOLIA_RPC")); + paymasterOwnerAddresses[uint256(Forks.BaseTestFork)] = openfortPaymasterOwnerTestnet; + paymasterAddresses[uint256(Forks.BaseTestFork)] = openfortPaymasterV2Testnet; // Fork: Beam testnet vm.createFork(vm.envString("BEAM_TESTNET_RPC")); diff --git a/script/checkDeposits.s.sol b/script/checkDeposits.s.sol index b80fffa..8bc2165 100644 --- a/script/checkDeposits.s.sol +++ b/script/checkDeposits.s.sol @@ -88,11 +88,11 @@ contract CheckDeposits is OpenfortForksConfig { console.log("Checking Paymaster and PaymasterOwner on BSC testnet:"); checkPaymasterDepositAndOwnerBalance(uint256(Forks.BscTestFork)); - console.log("Checking Paymaster and PaymasterOwner on Arbirtum Goerli testnet:"); + console.log("Checking Paymaster and PaymasterOwner on Arbirtum Sepolia testnet:"); checkPaymasterDepositAndOwnerBalance(uint256(Forks.ArbitrumTestFork)); - console.log("Checking Paymaster and PaymasterOwner on Base Goerli testnet:"); - checkPaymasterDepositAndOwnerBalance(uint256(Forks.BaseGoerliFork)); + console.log("Checking Paymaster and PaymasterOwner on Base Sepolia testnet:"); + checkPaymasterDepositAndOwnerBalance(uint256(Forks.BaseTestFork)); console.log("Checking Paymaster and PaymasterOwner on Beam testnet:"); checkPaymasterDepositAndOwnerBalance(uint256(Forks.BeamTestnetFork)); From b8312e0a94d2f0e1eb6a1471956ce8f5fb90b3c7 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 11 Dec 2023 17:18:55 +0100 Subject: [PATCH 61/83] Improved deployment scripts --- script/deployManagedAccounts.s.sol | 6 +- script/deployUpgradeableAccounts.s.sol | 87 ++++++++++++-------------- 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/script/deployManagedAccounts.s.sol b/script/deployManagedAccounts.s.sol index 3858c12..ce585e9 100644 --- a/script/deployManagedAccounts.s.sol +++ b/script/deployManagedAccounts.s.sol @@ -34,7 +34,9 @@ contract ManagedOpenfortDeploy is Script { LOCK_PERIOD, OPENFORT_GUARDIAN ); - + + vm.stopBroadcast(); + address accountImpl = openfortFactory.implementation(); console.log("Account implementation: ", accountImpl); @@ -42,7 +44,5 @@ contract ManagedOpenfortDeploy is Script { address firstAccountAddress = openfortFactory.createAccountWithNonce(deployAddress, "1", true); console.log(firstAccountAddress); console.log("First Account Address: ", firstAccountAddress); - - vm.stopBroadcast(); } } diff --git a/script/deployUpgradeableAccounts.s.sol b/script/deployUpgradeableAccounts.s.sol index 10c6db0..1a50937 100644 --- a/script/deployUpgradeableAccounts.s.sol +++ b/script/deployUpgradeableAccounts.s.sol @@ -6,50 +6,43 @@ import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPo import {UpgradeableOpenfortAccount} from "../contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; -// contract UpgradeableOpenfortDeploy is Script { -// uint256 internal deployPrivKey = vm.deriveKey(vm.envString("MNEMONIC_MC"), 0); -// // uint256 internal deployPrivKey = vm.envUint("PK"); -// address internal deployAddress = vm.addr(deployPrivKey); -// IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); - -// function run() public { -// bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); -// vm.startBroadcast(deployPrivKey); - -// UpgradeableOpenfortAccount upgradeableOpenfortAccount = new UpgradeableOpenfortAccount{salt: versionSalt}(); - -// UpgradeableOpenfortFactory upgradeableOpenfortFactory = -// new UpgradeableOpenfortFactory{salt: versionSalt}(address(entryPoint), address(upgradeableOpenfortAccount)); -// (upgradeableOpenfortFactory); -// // address account1 = upgradeableOpenfortFactory.accountImplementation(); - -// // The first call should create a new account, while the second will just return the corresponding account address -// address account2 = upgradeableOpenfortFactory.createAccountWithNonce(deployAddress, "1"); -// console.log( -// "Factory at address %s has created an account at address %s", address(upgradeableOpenfortFactory), account2 -// ); - -// // assert(account1 != account2); -// // address account3 = upgradeableOpenfortFactory.createAccountWithNonce(deployAddress, 3); -// // console.log( -// // "Factory at address %s has created an account at address %s", address(upgradeableOpenfortFactory), account3 -// // ); -// // assert(account2 != account3); -// // address account4 = upgradeableOpenfortFactory.createAccountWithNonce(deployAddress, 4); -// // console.log( -// // "Factory at address %s has created an account at address %s", address(upgradeableOpenfortFactory), account4 -// // ); -// // assert(account3 != account4); - -// //address account3 = upgradeableOpenfortFactory.createAccount(deployAddress, bytes("")); - -// //assert(account1 != account2); -// //assert(account2 == account3); - -// //UpgradeableOpenfortAccount newAccount = new UpgradeableOpenfortAccount(); - -// //UpgradeableOpenfortAccount(payable(account2)).upgradeTo(address(newAccount)); - -// vm.stopBroadcast(); -// } -// } +contract UpgradeableOpenfortDeploy is Script { + uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); + address internal deployAddress = vm.addr(deployPrivKey); + IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); + + uint256 private constant RECOVERY_PERIOD = 2 days; + uint256 private constant SECURITY_PERIOD = 1.5 days; + uint256 private constant SECURITY_WINDOW = 0.5 days; + uint256 private constant LOCK_PERIOD = 5 days; + address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_TESTNET"); + + function run() public { + bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); + vm.startBroadcast(deployPrivKey); + + // Create an acccount to serve as implementation + UpgradeableOpenfortAccount upgradeableOpenfortAccountImpl = new UpgradeableOpenfortAccount{salt: versionSalt}(); + // deploy account factory (beacon) + UpgradeableOpenfortFactory openfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( + deployAddress, + address(entryPoint), + address(upgradeableOpenfortAccountImpl), + RECOVERY_PERIOD, + SECURITY_PERIOD, + SECURITY_WINDOW, + LOCK_PERIOD, + OPENFORT_GUARDIAN + ); + + vm.stopBroadcast(); + + address accountImpl = openfortFactory.implementation(); + console.log("Account implementation: ", accountImpl); + + // Create an upgradeable account wallet and get its address + address firstAccountAddress = openfortFactory.createAccountWithNonce(deployAddress, "1", true); + console.log(firstAccountAddress); + console.log("First Account Address: ", firstAccountAddress); + } +} From 99c8d06687eb387000e78ada41ca4aeef01d8a1d Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 11 Dec 2023 17:26:24 +0100 Subject: [PATCH 62/83] Refactor deployment scripts --- script/{ => aux}/count-slocs.sh | 0 script/{ => aux}/gasProfile.sh | 0 script/deployManagedAccounts.s.sol | 13 ++++++++----- script/deployMock.s.sol | 8 +++----- script/deployOpenfortPaymasterV2.s.sol | 7 +++---- script/deployUpgradeableAccounts.s.sol | 9 ++++++--- 6 files changed, 20 insertions(+), 17 deletions(-) rename script/{ => aux}/count-slocs.sh (100%) rename script/{ => aux}/gasProfile.sh (100%) diff --git a/script/count-slocs.sh b/script/aux/count-slocs.sh similarity index 100% rename from script/count-slocs.sh rename to script/aux/count-slocs.sh diff --git a/script/gasProfile.sh b/script/aux/gasProfile.sh similarity index 100% rename from script/gasProfile.sh rename to script/aux/gasProfile.sh diff --git a/script/deployManagedAccounts.s.sol b/script/deployManagedAccounts.s.sol index ce585e9..b08832e 100644 --- a/script/deployManagedAccounts.s.sol +++ b/script/deployManagedAccounts.s.sol @@ -17,14 +17,17 @@ contract ManagedOpenfortDeploy is Script { uint256 private constant LOCK_PERIOD = 5 days; address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_TESTNET"); - function run() public { + function run() + public + returns (ManagedOpenfortAccount managedOpenfortAccountImpl, ManagedOpenfortFactory openfortFactory) + { bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); vm.startBroadcast(deployPrivKey); // Create an acccount to serve as implementation - ManagedOpenfortAccount managedOpenfortAccountImpl = new ManagedOpenfortAccount{salt: versionSalt}(); + managedOpenfortAccountImpl = new ManagedOpenfortAccount{salt: versionSalt}(); // deploy account factory (beacon) - ManagedOpenfortFactory openfortFactory = new ManagedOpenfortFactory{salt: versionSalt}( + openfortFactory = new ManagedOpenfortFactory{salt: versionSalt}( deployAddress, address(entryPoint), address(managedOpenfortAccountImpl), @@ -34,9 +37,9 @@ contract ManagedOpenfortDeploy is Script { LOCK_PERIOD, OPENFORT_GUARDIAN ); - + vm.stopBroadcast(); - + address accountImpl = openfortFactory.implementation(); console.log("Account implementation: ", accountImpl); diff --git a/script/deployMock.s.sol b/script/deployMock.s.sol index 8e7c638..d6bef29 100644 --- a/script/deployMock.s.sol +++ b/script/deployMock.s.sol @@ -9,14 +9,12 @@ contract DeployMock is Script { uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); address internal deployAddress = vm.addr(deployPrivKey); - function run() public { + function run() public returns (MockERC20 mockERC20, MockERC721 mockERC721) { bytes32 versionSalt = bytes32(0x0); vm.startBroadcast(deployPrivKey); - MockERC20 mockERC20 = new MockERC20{salt: versionSalt}(); - (mockERC20); - MockERC721 mockERC721 = new MockERC721{salt: versionSalt}(); - (mockERC721); + mockERC20 = new MockERC20{salt: versionSalt}(); + mockERC721 = new MockERC721{salt: versionSalt}(); vm.stopBroadcast(); } diff --git a/script/deployOpenfortPaymasterV2.s.sol b/script/deployOpenfortPaymasterV2.s.sol index 27e711b..bfcf235 100644 --- a/script/deployOpenfortPaymasterV2.s.sol +++ b/script/deployOpenfortPaymasterV2.s.sol @@ -6,17 +6,16 @@ import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPo import {OpenfortPaymasterV2} from "../contracts/paymaster/OpenfortPaymasterV2.sol"; contract OpenfortPaymasterV2Deploy is Script { - uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_MAINNET"); + uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); address internal deployAddress = vm.addr(deployPrivKey); IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); uint32 internal constant UNSTAKEDELAYSEC = 8600; - function run() public { + function run() public returns (OpenfortPaymasterV2 openfortPaymaster) { bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); vm.startBroadcast(deployPrivKey); - OpenfortPaymasterV2 openfortPaymaster = new OpenfortPaymasterV2{salt: versionSalt}(entryPoint, deployAddress); - + openfortPaymaster = new OpenfortPaymasterV2{salt: versionSalt}(entryPoint, deployAddress); entryPoint.depositTo{value: 1.5 ether}(address(openfortPaymaster)); openfortPaymaster.addStake{value: 0.015 ether}(UNSTAKEDELAYSEC); diff --git a/script/deployUpgradeableAccounts.s.sol b/script/deployUpgradeableAccounts.s.sol index 1a50937..bf7496d 100644 --- a/script/deployUpgradeableAccounts.s.sol +++ b/script/deployUpgradeableAccounts.s.sol @@ -17,14 +17,17 @@ contract UpgradeableOpenfortDeploy is Script { uint256 private constant LOCK_PERIOD = 5 days; address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_TESTNET"); - function run() public { + function run() + public + returns (UpgradeableOpenfortAccount upgradeableOpenfortAccountImpl, UpgradeableOpenfortFactory openfortFactory) + { bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); vm.startBroadcast(deployPrivKey); // Create an acccount to serve as implementation - UpgradeableOpenfortAccount upgradeableOpenfortAccountImpl = new UpgradeableOpenfortAccount{salt: versionSalt}(); + upgradeableOpenfortAccountImpl = new UpgradeableOpenfortAccount{salt: versionSalt}(); // deploy account factory (beacon) - UpgradeableOpenfortFactory openfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( + openfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( deployAddress, address(entryPoint), address(upgradeableOpenfortAccountImpl), From 29ee767a36f95e9eb20f74b3089eccadc9375615 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 11:53:52 +0100 Subject: [PATCH 63/83] Improved scripts, tests and initial guardian setup --- package.json | 4 +- script/aux/checkOrDeployEntryPoint.sol | 30 ++ script/deployManagedAccounts.s.sol | 13 +- script/deployMock.s.sol | 4 +- script/deployOpenfortPaymasterV2.s.sol | 7 +- script/deployUpgradeableAccounts.s.sol | 21 +- test/foundry/core/OpenfortBaseTest.t.sol | 53 +- .../erc6551/ERC6551OpenfortAccountTest.sol | 88 ++- .../managed/ManagedOpenfortAccountTest.t.sol | 435 +++++++-------- .../UpgradeableOpenfortAccountTest.t.sol | 501 ++++++++---------- .../paymaster/OpenfortPaymasterV2Test.t.sol | 298 +++++------ 11 files changed, 700 insertions(+), 754 deletions(-) create mode 100644 script/aux/checkOrDeployEntryPoint.sol diff --git a/package.json b/package.json index 2764cf1..f5df1fc 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "@openfort/openfort-contracts", - "version": "0.5.0", + "version": "0.5.1", "description": "Official Contracts of the Openfort Project", "license": "GPL-3.0", "scripts": { "build": "npm run clean-all && npm run compile", "clean-all": "npx hardhat clean && forge clean && rm -rf cache/ broadcast/", "compile": "npx hardhat compile", - "test-all": "npx hardhat test && forge test" + "test": "source .env && forge test" }, "devDependencies": { "@nomicfoundation/hardhat-foundry": "^1.0.1", diff --git a/script/aux/checkOrDeployEntryPoint.sol b/script/aux/checkOrDeployEntryPoint.sol new file mode 100644 index 0000000..1267162 --- /dev/null +++ b/script/aux/checkOrDeployEntryPoint.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {Script} from "forge-std/Script.sol"; +import {IEntryPoint, EntryPoint} from "account-abstraction/core/EntryPoint.sol"; + +contract CheckOrDeployEntryPoint is Script { + uint256 private ANVIL_CHAINID = 31337; + + function checkOrDeployEntryPoint() public returns (IEntryPoint entryPoint) { + uint256 chainId; + assembly { + chainId := chainid() + } + // If we are in a fork + if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { + entryPoint = IEntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); + } + // If not a fork, deploy entryPoint (at correct address) + else if (chainId == ANVIL_CHAINID) { + EntryPoint entryPointAux = new EntryPoint(); + bytes memory code = address(entryPointAux).code; + address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); + vm.etch(targetAddr, code); + entryPoint = IEntryPoint(payable(targetAddr)); + } else { + revert("No EntryPoint in this chain"); + } + } +} diff --git a/script/deployManagedAccounts.s.sol b/script/deployManagedAccounts.s.sol index b08832e..c6853ff 100644 --- a/script/deployManagedAccounts.s.sol +++ b/script/deployManagedAccounts.s.sol @@ -5,25 +5,28 @@ import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; import {ManagedOpenfortAccount} from "../contracts/core/managed/ManagedOpenfortAccount.sol"; import {ManagedOpenfortFactory} from "../contracts/core/managed/ManagedOpenfortFactory.sol"; +import {CheckOrDeployEntryPoint} from "script/aux/checkOrDeployEntryPoint.sol"; -contract ManagedOpenfortDeploy is Script { +contract ManagedOpenfortDeploy is Script, CheckOrDeployEntryPoint { uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); address internal deployAddress = vm.addr(deployPrivKey); - IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); + IEntryPoint internal entryPoint; uint256 private constant RECOVERY_PERIOD = 2 days; uint256 private constant SECURITY_PERIOD = 1.5 days; uint256 private constant SECURITY_WINDOW = 0.5 days; uint256 private constant LOCK_PERIOD = 5 days; - address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_TESTNET"); + uint256 internal guardianPrivKey = vm.envUint("PK_GUARDIAN_TESTNET"); + address internal guardianAddress = vm.addr(guardianPrivKey); function run() public returns (ManagedOpenfortAccount managedOpenfortAccountImpl, ManagedOpenfortFactory openfortFactory) { bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); - vm.startBroadcast(deployPrivKey); + entryPoint = checkOrDeployEntryPoint(); + vm.startBroadcast(deployPrivKey); // Create an acccount to serve as implementation managedOpenfortAccountImpl = new ManagedOpenfortAccount{salt: versionSalt}(); // deploy account factory (beacon) @@ -35,7 +38,7 @@ contract ManagedOpenfortDeploy is Script { SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, - OPENFORT_GUARDIAN + guardianAddress ); vm.stopBroadcast(); diff --git a/script/deployMock.s.sol b/script/deployMock.s.sol index d6bef29..2aefd83 100644 --- a/script/deployMock.s.sol +++ b/script/deployMock.s.sol @@ -4,17 +4,19 @@ pragma solidity =0.8.19; import {Script} from "forge-std/Script.sol"; import {MockERC20} from "../contracts/mock/MockERC20.sol"; import {MockERC721} from "../contracts/mock/MockERC721.sol"; +import {MockERC1155} from "../contracts/mock/MockERC1155.sol"; contract DeployMock is Script { uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); address internal deployAddress = vm.addr(deployPrivKey); - function run() public returns (MockERC20 mockERC20, MockERC721 mockERC721) { + function run() public returns (MockERC20 mockERC20, MockERC721 mockERC721, MockERC1155 mockERC1155) { bytes32 versionSalt = bytes32(0x0); vm.startBroadcast(deployPrivKey); mockERC20 = new MockERC20{salt: versionSalt}(); mockERC721 = new MockERC721{salt: versionSalt}(); + mockERC1155 = new MockERC1155{salt: versionSalt}(); vm.stopBroadcast(); } diff --git a/script/deployOpenfortPaymasterV2.s.sol b/script/deployOpenfortPaymasterV2.s.sol index bfcf235..6ea2a60 100644 --- a/script/deployOpenfortPaymasterV2.s.sol +++ b/script/deployOpenfortPaymasterV2.s.sol @@ -4,15 +4,18 @@ pragma solidity =0.8.19; import {Script} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; import {OpenfortPaymasterV2} from "../contracts/paymaster/OpenfortPaymasterV2.sol"; +import {CheckOrDeployEntryPoint} from "script/aux/checkOrDeployEntryPoint.sol"; -contract OpenfortPaymasterV2Deploy is Script { +contract OpenfortPaymasterV2Deploy is Script, CheckOrDeployEntryPoint { uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); address internal deployAddress = vm.addr(deployPrivKey); - IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); + IEntryPoint internal entryPoint; uint32 internal constant UNSTAKEDELAYSEC = 8600; function run() public returns (OpenfortPaymasterV2 openfortPaymaster) { bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); + entryPoint = checkOrDeployEntryPoint(); + vm.startBroadcast(deployPrivKey); openfortPaymaster = new OpenfortPaymasterV2{salt: versionSalt}(entryPoint, deployAddress); diff --git a/script/deployUpgradeableAccounts.s.sol b/script/deployUpgradeableAccounts.s.sol index bf7496d..908d0a7 100644 --- a/script/deployUpgradeableAccounts.s.sol +++ b/script/deployUpgradeableAccounts.s.sol @@ -5,28 +5,39 @@ import {Script, console} from "forge-std/Script.sol"; import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; import {UpgradeableOpenfortAccount} from "../contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; import {UpgradeableOpenfortFactory} from "../contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; +import {CheckOrDeployEntryPoint} from "script/aux/checkOrDeployEntryPoint.sol"; + +contract UpgradeableOpenfortDeploy is Script, CheckOrDeployEntryPoint { + address private CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C; -contract UpgradeableOpenfortDeploy is Script { uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); address internal deployAddress = vm.addr(deployPrivKey); - IEntryPoint internal entryPoint = IEntryPoint((payable(vm.envAddress("ENTRY_POINT_ADDRESS")))); + IEntryPoint internal entryPoint; uint256 private constant RECOVERY_PERIOD = 2 days; uint256 private constant SECURITY_PERIOD = 1.5 days; uint256 private constant SECURITY_WINDOW = 0.5 days; uint256 private constant LOCK_PERIOD = 5 days; - address private OPENFORT_GUARDIAN = vm.envAddress("PAYMASTER_OWNER_TESTNET"); + uint256 internal guardianPrivKey = vm.envUint("PK_GUARDIAN_TESTNET"); + address internal guardianAddress = vm.addr(guardianPrivKey); + + event AccountImplementationDeployed(address indexed creator); function run() public returns (UpgradeableOpenfortAccount upgradeableOpenfortAccountImpl, UpgradeableOpenfortFactory openfortFactory) { bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); + entryPoint = checkOrDeployEntryPoint(); + vm.startBroadcast(deployPrivKey); + // deploy upgradeable account implementation + vm.expectEmit(true, true, false, true); + emit AccountImplementationDeployed(CREATE2_DEPLOYER); // Create an acccount to serve as implementation upgradeableOpenfortAccountImpl = new UpgradeableOpenfortAccount{salt: versionSalt}(); - // deploy account factory (beacon) + // deploy account factory openfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( deployAddress, address(entryPoint), @@ -35,7 +46,7 @@ contract UpgradeableOpenfortDeploy is Script { SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, - OPENFORT_GUARDIAN + guardianAddress ); vm.stopBroadcast(); diff --git a/test/foundry/core/OpenfortBaseTest.t.sol b/test/foundry/core/OpenfortBaseTest.t.sol index fb01741..d3ae430 100644 --- a/test/foundry/core/OpenfortBaseTest.t.sol +++ b/test/foundry/core/OpenfortBaseTest.t.sol @@ -4,19 +4,22 @@ pragma solidity =0.8.19; import {Test, console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; -import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +import {IEntryPoint, EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {MockERC20} from "contracts/mock/MockERC20.sol"; import {MockERC721} from "contracts/mock/MockERC721.sol"; import {MockERC1155} from "contracts/mock/MockERC1155.sol"; import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; +import {DeployMock} from "script/deployMock.s.sol"; +import {SimpleNFT} from "contracts/mock/SimpleNFT.sol"; +import {CheckOrDeployEntryPoint} from "script/aux/checkOrDeployEntryPoint.sol"; -contract OpenfortBaseTest is Test { +contract OpenfortBaseTest is Test, CheckOrDeployEntryPoint { using ECDSA for bytes32; bytes32 public versionSalt; - EntryPoint public entryPoint; + IEntryPoint public entryPoint; address public accountAddress; TestCounter public testCounter; @@ -25,11 +28,8 @@ contract OpenfortBaseTest is Test { MockERC1155 public mockERC1155; // Testing addresses - address public factoryAdmin; - uint256 public factoryAdminPKey; - - address public accountAdmin; - uint256 public accountAdminPKey; + address public openfortAdmin; + uint256 public openfortAdminPKey; address payable public beneficiary = payable(makeAddr("beneficiary")); @@ -47,11 +47,11 @@ contract OpenfortBaseTest is Test { uint256 public constant SECURITY_PERIOD = 1.5 days; uint256 public constant SECURITY_WINDOW = 0.5 days; uint256 public constant LOCK_PERIOD = 5 days; - address public OPENFORT_GUARDIAN; - uint256 public OPENFORT_GUARDIAN_PKEY; + address public openfortGuardian; + uint256 public openfortGuardianKey; event AccountImplementationDeployed(address indexed creator); - event AccountCreated(address indexed account, address indexed accountAdmin); + event AccountCreated(address indexed account, address indexed openfortAdmin); event GuardianProposed(address indexed guardian, uint256 executeAfter); event GuardianProposalCancelled(address indexed guardian); event GuardianRevocationRequested(address indexed guardian, uint256 executeAfter); @@ -75,6 +75,25 @@ contract OpenfortBaseTest is Test { error InvalidRecoverySignatures(); error InvalidSignatureAmount(); + function setUp() public virtual { + versionSalt = vm.envBytes32("VERSION_SALT"); + entryPoint = checkOrDeployEntryPoint(); + + // Setup and fund signers + openfortAdminPKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); + openfortAdmin = vm.addr(openfortAdminPKey); + vm.deal(openfortAdmin, 100 ether); + + openfortGuardianKey = vm.envUint("PK_GUARDIAN_TESTNET"); + openfortGuardian = vm.addr(openfortGuardianKey); + + DeployMock deployMock = new DeployMock(); + (mockERC20, mockERC721, mockERC1155) = deployMock.run(); + + // deploy a new TestCounter + testCounter = new TestCounter{salt: versionSalt}(); + } + /* * Auxiliary function to generate a userOP */ @@ -102,7 +121,7 @@ contract OpenfortBaseTest is Test { }); // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); + bytes32 opHash = entryPoint.getUserOpHash(op); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); @@ -160,4 +179,14 @@ contract OpenfortBaseTest is Test { */ event Deposited(address indexed account, uint256 totalDeposit); + + /* + * Test for coverage purposes. + * SimpleNFT is an NFT contract used by Openfort in some internal tests + */ + function testSimpleNFT() public { + SimpleNFT simpleNFT = new SimpleNFT(); + simpleNFT.mint(openfortAdmin); + assertEq(simpleNFT.balanceOf(openfortAdmin), 1); + } } diff --git a/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol index 51d7e43..0f5432d 100644 --- a/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol +++ b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol @@ -7,9 +7,7 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {ERC6551Registry, IERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; -import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {TestCounter} from "account-abstraction/test/TestCounter.sol"; -import {MockERC721} from "contracts/mock/MockERC721.sol"; +import {IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {IERC6551Account} from "erc6551/src/interfaces/IERC6551Account.sol"; import {IERC6551Executable} from "erc6551/src/interfaces/IERC6551Executable.sol"; import {ERC6551OpenfortAccount} from "contracts/core/erc6551/ERC6551OpenfortAccount.sol"; @@ -26,18 +24,13 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { /** * @notice Initialize the StaticOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the mockNFT - * - accountAdmin is the account used to deploy new static accounts + * - openfortAdmin is the deployer (and owner) of the mock tokens + * - openfortAdmin is the account used to deploy new static accounts * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps */ - function setUp() public { - versionSalt = bytes32(0x0); - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); + function setUp() public override { + super.setUp(); uint256 chainId; assembly { @@ -45,20 +38,7 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { } console.log("ChainId:", chainId); - vm.startPrank(factoryAdmin); - - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at the correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } + vm.startPrank(openfortAdmin); // If we are in a fork if (vm.envAddress("ERC6551_REGISTRY_ADDRESS").code.length > 0) { @@ -74,19 +54,13 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { erc6551Registry = ERC6551Registry(payable(targetAddr)); } - // deploy a new MockERC721 collection - mockERC721 = new MockERC721{salt: versionSalt}(); - erc6551OpenfortAccountImpl = new ERC6551OpenfortAccount{salt: versionSalt}(); accountAddress = erc6551Registry.createAccount( address(erc6551OpenfortAccountImpl), versionSalt, chainId, address(mockERC721), 1 ); - mockERC721.mint(accountAdmin, 1); - - // deploy a new TestCounter - testCounter = new TestCounter{salt: versionSalt}(); + mockERC721.mint(openfortAdmin, 1); vm.stopPrank(); @@ -188,7 +162,7 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { assertEq(deposit, 1 ether); // We can ALSO add deposit by calling the EntryPoint depositTo() function - vm.prank(accountAdmin); + vm.prank(openfortAdmin); erc6551OpenfortAccount.execute{value: 1 ether}( address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) ); @@ -203,7 +177,7 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { */ function testOwner() public { assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); - assertEq(erc6551OpenfortAccount.owner(), accountAdmin); + assertEq(erc6551OpenfortAccount.owner(), openfortAdmin); vm.chainId(9999999); assertEq(erc6551OpenfortAccount.owner(), address(0)); @@ -239,12 +213,12 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { bytes4 isValid = erc6551OpenfortAccount.isValidSigner(vm.addr(2), ""); assertEq(isValid, 0); - isValid = erc6551OpenfortAccount.isValidSigner(accountAdmin, ""); + isValid = erc6551OpenfortAccount.isValidSigner(openfortAdmin, ""); assertEq(isValid, erc6551OpenfortAccount.isValidSigner.selector); erc6551OpenfortAccount.initialize(); - isValid = erc6551OpenfortAccount.isValidSigner(accountAdmin, ""); + isValid = erc6551OpenfortAccount.isValidSigner(openfortAdmin, ""); assertEq(isValid, erc6551OpenfortAccount.isValidSigner.selector); } @@ -254,11 +228,11 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { */ function testNotOwner() public { // Burning the NFT - vm.prank(accountAdmin); - mockERC721.transferFrom(accountAdmin, address(1), 1); + vm.prank(openfortAdmin); + mockERC721.transferFrom(openfortAdmin, address(1), 1); assertEq(erc6551OpenfortAccount.owner(), mockERC721.ownerOf(1)); - assertNotEq(erc6551OpenfortAccount.owner(), accountAdmin); + assertNotEq(erc6551OpenfortAccount.owner(), openfortAdmin); assertEq(erc6551OpenfortAccount.owner(), address(1)); } @@ -266,18 +240,18 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { erc6551OpenfortAccount.initialize(); vm.deal(accountAddress, 1 ether); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert(ERC6551OpenfortAccount.OperationNotAllowed.selector); erc6551OpenfortAccount.execute(payable(vm.addr(2)), 0.5 ether, "", 1); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); erc6551OpenfortAccount.execute(payable(vm.addr(2)), 0.5 ether, "", 0); assertEq(accountAddress.balance, 0.5 ether); assertEq(vm.addr(2).balance, 0.5 ether); assertEq(erc6551OpenfortAccount.state(), 2); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); erc6551OpenfortAccount.execute(payable(vm.addr(2)), 0.5 ether, ""); assertEq(accountAddress.balance, 0 ether); @@ -301,7 +275,7 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { callData[0] = ""; callData[1] = ""; - vm.prank(accountAdmin); + vm.prank(openfortAdmin); erc6551OpenfortAccount.executeBatch(targets, values, callData); assertEq(accountAddress.balance, 0.5 ether); @@ -318,7 +292,7 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" - vm.prank(accountAdmin); + vm.prank(openfortAdmin); erc6551OpenfortAccount.execute(address(testCounter), 0, abi.encodeWithSignature("count()")); // Verify that the counter has increased @@ -336,7 +310,7 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, openfortAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); entryPoint.depositTo{value: 1 ether}(accountAddress); @@ -361,19 +335,19 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(OpenfortErrorsAndEvents.NotOwner.selector); erc6551OpenfortAccount.updateEntryPoint(vm.addr(2)); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert(OpenfortErrorsAndEvents.ZeroAddressNotAllowed.selector); erc6551OpenfortAccount.updateEntryPoint(address(0)); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); erc6551OpenfortAccount.updateEntryPoint(vm.addr(2)); // Weird behaviour, but not necessarily bad; if the user changes the EntryPoint address // before initializing, the EntryPoint is set to the default - vm.prank(accountAdmin); + vm.prank(openfortAdmin); erc6551OpenfortAccount.initialize(); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); erc6551OpenfortAccount.updateEntryPoint(vm.addr(2)); } @@ -381,9 +355,9 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { * Test onERC721Received() */ function testSafeTransferFrom() public { - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert("Cannot own yourself"); - mockERC721.safeTransferFrom(accountAdmin, address(erc6551OpenfortAccount), 1); + mockERC721.safeTransferFrom(openfortAdmin, address(erc6551OpenfortAccount), 1); uint256 _chainId; assembly { @@ -394,12 +368,12 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { ); // Try with token ID 2 not minted yet - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert("ERC721: invalid token ID"); - mockERC721.safeTransferFrom(accountAdmin, accountAddress2, 1); + mockERC721.safeTransferFrom(openfortAdmin, accountAddress2, 1); - mockERC721.mint(accountAdmin, 2); - vm.prank(accountAdmin); - mockERC721.safeTransferFrom(accountAdmin, accountAddress2, 1); + mockERC721.mint(openfortAdmin, 2); + vm.prank(openfortAdmin); + mockERC721.safeTransferFrom(openfortAdmin, accountAddress2, 1); } } diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index abc4f5f..328ff4f 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -4,7 +4,7 @@ pragma solidity =0.8.19; import {console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; -import {EntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +import {IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; import {ManagedOpenfortAccount} from "contracts/core/managed/ManagedOpenfortAccount.sol"; @@ -12,11 +12,12 @@ import {ManagedOpenfortFactory} from "contracts/core/managed/ManagedOpenfortFact import {ManagedOpenfortProxy} from "contracts/core/managed/ManagedOpenfortProxy.sol"; import {MockV2ManagedOpenfortAccount} from "contracts/mock/MockV2ManagedOpenfortAccount.sol"; import {IBaseOpenfortFactory} from "contracts/interfaces/IBaseOpenfortFactory.sol"; -import {OpenfortBaseTest, MockERC20, MockERC721, MockERC1155} from "../OpenfortBaseTest.t.sol"; +import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {ManagedOpenfortDeploy} from "script/deployManagedAccounts.s.sol"; contract ManagedOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; @@ -27,56 +28,20 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { /** * @notice Initialize the ManagedOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the managedOpenfortAccountImpl and openfortFactory/Beacon - * - accountAdmin is the account used to deploy new managed accounts using the factory + * - openfortAdmin is the deployer (and owner) of the managedOpenfortAccountImpl and openfortFactory/Beacon + * - openfortAdmin is the account used to deploy new managed accounts using the factory * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps */ - function setUp() public { - versionSalt = bytes32(0x0); - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); - - vm.startPrank(factoryAdmin); - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } - // deploy account implementation - managedOpenfortAccountImpl = new ManagedOpenfortAccount{salt: versionSalt}(); - // deploy account factory (beacon) - openfortFactory = new ManagedOpenfortFactory{salt: versionSalt}( - factoryAdmin, - address(entryPoint), - address(managedOpenfortAccountImpl), - RECOVERY_PERIOD, - SECURITY_PERIOD, - SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN - ); + function setUp() public override { + super.setUp(); + + ManagedOpenfortDeploy managedOpenfortDeploy = new ManagedOpenfortDeploy(); + (managedOpenfortAccountImpl, openfortFactory) = managedOpenfortDeploy.run(); + // Create a managed account wallet and get its address - accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1", true); - - // deploy a new TestCounter - testCounter = new TestCounter{salt: versionSalt}(); - // deploy a new MockERC20 (ERC20) - mockERC20 = new MockERC20{salt: versionSalt}(); - mockERC721 = new MockERC721{salt: versionSalt}(); - mockERC1155 = new MockERC1155{salt: versionSalt}(); - vm.stopPrank(); + vm.prank(openfortAdmin); + accountAddress = openfortFactory.createAccountWithNonce(openfortAdmin, "1", true); } /* @@ -87,29 +52,29 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { openfortFactory.addStake{value: 1 ether}(10); vm.expectRevert("no stake specified"); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.addStake(10); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.addStake{value: 1 ether}(10); vm.expectRevert("Ownable: caller is not the owner"); openfortFactory.unlockStake(); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.unlockStake(); vm.expectRevert("Ownable: caller is not the owner"); - openfortFactory.withdrawStake(payable(factoryAdmin)); + openfortFactory.withdrawStake(payable(openfortAdmin)); vm.expectRevert("Stake withdrawal is not due"); - vm.prank(factoryAdmin); - openfortFactory.withdrawStake(payable(factoryAdmin)); + vm.prank(openfortAdmin); + openfortFactory.withdrawStake(payable(openfortAdmin)); skip(11); - vm.prank(factoryAdmin); - openfortFactory.withdrawStake(payable(factoryAdmin)); + vm.prank(openfortAdmin); + openfortFactory.withdrawStake(payable(openfortAdmin)); } /* @@ -119,13 +84,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(openfortFactory.implementation(), address(managedOpenfortAccountImpl)); vm.expectRevert("Initializable: contract is already initialized"); managedOpenfortAccountImpl.initialize( - accountAdmin, + openfortAdmin, address(entryPoint), RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, - OPENFORT_GUARDIAN + openfortGuardian ); } @@ -134,24 +99,24 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testCreateAccountWithNonceViaFactory() public { // Get the counterfactual address - vm.prank(factoryAdmin); - address account2 = openfortFactory.getAddressWithNonce(accountAdmin, "2"); + vm.prank(openfortAdmin); + address account2 = openfortFactory.getAddressWithNonce(openfortAdmin, "2"); // Expect that we will see an event containing the account and admin vm.expectEmit(true, true, false, true); - emit AccountCreated(account2, accountAdmin); + emit AccountCreated(account2, openfortAdmin); // Deploy a managed account to the counterfactual address - vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2", true); + vm.prank(openfortAdmin); + openfortFactory.createAccountWithNonce(openfortAdmin, "2", true); // Calling it again should just return the address and not create another account - vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2", true); + vm.prank(openfortAdmin); + openfortFactory.createAccountWithNonce(openfortAdmin, "2", true); // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); - assertEq(account2, openfortFactory.getAddressWithNonce(accountAdmin, "2")); + vm.prank(openfortAdmin); + assertEq(account2, openfortFactory.getAddressWithNonce(openfortAdmin, "2")); } /* @@ -159,27 +124,27 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { */ function testFuzzCreateAccountWithNonceViaFactory(address _adminAddress, bytes32 _nonce) public { // Get the counterfactual address - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); address account2 = openfortFactory.getAddressWithNonce(_adminAddress, _nonce); // Expect that we will see an event containing the account and admin if (_adminAddress == address(0)) { vm.expectRevert(); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); } else { vm.expectEmit(true, true, false, true); emit AccountCreated(account2, _adminAddress); // Deploy a managed account to the counterfactual address - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); // Calling it again should just return the address and not create another account - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); assertEq(account2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); } } @@ -194,29 +159,29 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // revert(); // Make sure the smart account does not have any code yet - address account2 = openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); + address account2 = openfortFactory.getAddressWithNonce(openfortAdmin, bytes32("2")); assertEq(account2.code.length, 0); bytes memory initCallData = - abi.encodeWithSignature("createAccountWithNonce(address,bytes32,bool)", accountAdmin, bytes32("2"), true); + abi.encodeWithSignature("createAccountWithNonce(address,bytes32,bool)", openfortAdmin, bytes32("2"), true); bytes memory initCode = abi.encodePacked(abi.encodePacked(address(openfortFactory)), initCallData); UserOperation[] memory userOpCreateAccount = - _setupUserOpExecute(account2, accountAdminPKey, initCode, address(0), 0, bytes("")); + _setupUserOpExecute(account2, openfortAdminPKey, initCode, address(0), 0, bytes("")); // vm.expectRevert(); // entryPoint.simulateValidation(userOpCreateAccount[0]); // Expect that we will see an event containing the account and admin vm.expectEmit(true, true, false, true); - emit AccountCreated(account2, accountAdmin); + emit AccountCreated(account2, openfortAdmin); entryPoint.handleOps(userOpCreateAccount, beneficiary); // Make sure the smart account does have some code now assert(account2.code.length > 0); // Make sure the counterfactual address has not been altered - assertEq(account2, openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); + assertEq(account2, openfortFactory.getAddressWithNonce(openfortAdmin, bytes32("2"))); } /* @@ -227,7 +192,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the managed account wallet (deployer) call "count" - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).execute( address(testCounter), 0, abi.encodeWithSignature("count()") ); @@ -245,7 +210,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, openfortAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); entryPoint.depositTo{value: 1 ether}(accountAddress); @@ -277,7 +242,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(accountAddress, accountAdminPKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, openfortAdminPKey, bytes(""), targets, values, callData); entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); @@ -324,7 +289,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey( sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist ); @@ -357,7 +322,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOp( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", @@ -406,7 +371,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOp( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", @@ -475,7 +440,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; vm.warp(100); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -504,9 +469,9 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( @@ -536,7 +501,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // We are now in block 100, but our session key is valid until block 150 vm.warp(100); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -575,7 +540,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // We are now in block 100, but our session key is valid until block 150 vm.warp(100); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); uint256 count = 3; @@ -613,7 +578,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); vm.prank(beneficiary); ManagedOpenfortAccount(payable(accountAddress)).revokeSessionKey(sessionKey); @@ -644,7 +609,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -672,7 +637,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory whitelist = new address[](11); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -701,7 +666,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); // Verify that the registered key is not a MasterKey but has whitelisting @@ -748,7 +713,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); // Verify that the registered key is not a MasterKey but has whitelisting @@ -798,7 +763,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { whitelist[i] = address(testCounter); } vm.expectRevert("Whitelist too big"); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); } @@ -815,7 +780,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(accountAddress); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -844,7 +809,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(accountAddress); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); uint256 count = 3; @@ -879,7 +844,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { /* * Change the owner of an account and call TestCounter directly. * Important use-case: - * 1- accountAdmin is Openfort's master wallet and is managing the account of the user. + * 1- openfortAdmin is Openfort's master wallet and is managing the account of the user. * 2- The user claims the ownership of the account to Openfort so Openfort calls * transferOwnership() to the account. * 3- The user has to "officially" claim the ownership of the account by directly @@ -888,24 +853,24 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * 5- Test that the new owner can directly interact with the account and make it call the testCounter contract. */ function testChangeOwnershipAndCountDirect() public { - address accountAdmin2; - uint256 accountAdmin2PKey; - (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); + address openfortAdmin2; + uint256 openfortAdmin2PKey; + (openfortAdmin2, openfortAdmin2PKey) = makeAddrAndKey("openfortAdmin2"); - assertEq(ManagedOpenfortAccount(payable(accountAddress)).owner(), accountAdmin); + assertEq(ManagedOpenfortAccount(payable(accountAddress)).owner(), openfortAdmin); vm.expectRevert("Ownable: caller is not the owner"); - ManagedOpenfortAccount(payable(accountAddress)).transferOwnership(accountAdmin2); + ManagedOpenfortAccount(payable(accountAddress)).transferOwnership(openfortAdmin2); - vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(accountAddress)).transferOwnership(accountAdmin2); - vm.prank(accountAdmin2); + vm.prank(openfortAdmin); + ManagedOpenfortAccount(payable(accountAddress)).transferOwnership(openfortAdmin2); + vm.prank(openfortAdmin2); ManagedOpenfortAccount(payable(accountAddress)).acceptOwnership(); // Verify that the counter is still set to 0 assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the managed account wallet (deployer) call "count" - vm.prank(accountAdmin2); + vm.prank(openfortAdmin2); ManagedOpenfortAccount(payable(accountAddress)).execute( address(testCounter), 0, abi.encodeWithSignature("count()") ); @@ -918,20 +883,20 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Change the owner of an account and call TestCounter though the Entrypoint */ function testChangeOwnershipAndCountEntryPoint() public { - address accountAdmin2; - uint256 accountAdmin2PKey; - (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); + address openfortAdmin2; + uint256 openfortAdmin2PKey; + (openfortAdmin2, openfortAdmin2PKey) = makeAddrAndKey("openfortAdmin2"); - vm.prank(accountAdmin); - ManagedOpenfortAccount(payable(accountAddress)).transferOwnership(accountAdmin2); - vm.prank(accountAdmin2); + vm.prank(openfortAdmin); + ManagedOpenfortAccount(payable(accountAddress)).transferOwnership(openfortAdmin2); + vm.prank(openfortAdmin2); ManagedOpenfortAccount(payable(accountAddress)).acceptOwnership(); // Verify that the counter is still set to 0 assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - accountAddress, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, openfortAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); entryPoint.depositTo{value: 1 ether}(accountAddress); @@ -956,7 +921,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -978,7 +943,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { function testReceiveNativeToken() public { assertEq(address(accountAddress).balance, 0); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); (bool success,) = payable(accountAddress).call{value: 1000}(""); assert(success); assertEq(address(accountAddress).balance, 1000); @@ -991,16 +956,16 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { uint256 value = 1000; assertEq(address(accountAddress).balance, 0); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); (bool success,) = payable(accountAddress).call{value: value}(""); assertEq(address(accountAddress).balance, value); assert(success); assertEq(beneficiary.balance, 0); UserOperation[] memory userOp = - _setupUserOpExecute(accountAddress, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); + _setupUserOpExecute(accountAddress, openfortAdminPKey, bytes(""), address(beneficiary), value, bytes("")); - EntryPoint(entryPoint).handleOps(userOp, beneficiary); + entryPoint.handleOps(userOp, beneficiary); assertEq(beneficiary.balance, value); } @@ -1012,7 +977,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // assertEq(testCounter.counters(accountAddress), 0); // UserOperation[] memory userOp = _setupUserOpExecute( - // account, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + // account, openfortAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") // ); // entryPoint.depositTo{value: 1 ether}(accountAddress); @@ -1024,10 +989,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // // Test addStake. Make sure it checks for owner and alue passed. // vm.expectRevert("Ownable: caller is not the owner"); // openfortFactory.addStake{value: 10000000000000000}(99); - // vm.prank(factoryAdmin); + // vm.prank(openfortAdmin); // vm.expectRevert("no stake specified"); // openfortFactory.addStake(99); - // vm.prank(factoryAdmin); + // vm.prank(openfortAdmin); // openfortFactory.addStake{value: 10000000000000000}(99); // // expectRevert as simulateValidation() always reverts @@ -1074,11 +1039,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { openfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); vm.expectRevert(IBaseOpenfortFactory.NotAContract.selector); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.upgradeTo(address(0)); // Finally upgrade - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.upgradeTo(address(mockV2ManagedOpenfortAccount)); // Try to use the old and new implementation before upgrade (should always behave with current values) @@ -1107,13 +1072,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { function testFailIsValidSignature() public { bytes32 hash = keccak256("Signed by Owner"); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, hash); address signer = ecrecover(hash, v, r, s); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes memory signature = abi.encodePacked(r, s, v); signer = ECDSA.recover(hash, signature); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes4 valid = ManagedOpenfortAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! @@ -1123,13 +1088,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { function testFailIsValidSignatureMessage() public { bytes32 hash = keccak256("Signed by Owner"); bytes32 hashMessage = hash.toEthSignedMessageHash(); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hashMessage); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, hashMessage); address signer = ecrecover(hashMessage, v, r, s); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes memory signature = abi.encodePacked(r, s, v); signer = ECDSA.recover(hashMessage, signature); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes4 valid = ManagedOpenfortAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! @@ -1168,11 +1133,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) ); - bytes memory signature = getEIP712SignatureFrom(accountAddress, structHash, accountAdminPKey); + bytes memory signature = getEIP712SignatureFrom(accountAddress, structHash, openfortAdminPKey); bytes32 hash712 = domainSeparator.toTypedDataHash(structHash); address signer = hash712.recover(signature); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes4 valid = ManagedOpenfortAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, MAGICVALUE); // SHOULD PASS @@ -1194,14 +1159,14 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(MustBeGuardian.selector); openfortAccount.lock(); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.lock(); assertEq(openfortAccount.isLocked(), true); assertEq(openfortAccount.getLock(), block.timestamp + LOCK_PERIOD); vm.expectRevert(AccountLocked.selector); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.lock(); // Automatically unlock @@ -1222,7 +1187,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(MustBeGuardian.selector); openfortAccount.lock(); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.lock(); assertEq(openfortAccount.isLocked(), true); @@ -1234,14 +1199,14 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { openfortAccount.unlock(); assertEq(openfortAccount.isLocked(), true); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.unlock(); assertEq(openfortAccount.isLocked(), false); assertEq(openfortAccount.getLock(), 0); vm.expectRevert(AccountNotLocked.selector); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.unlock(); } @@ -1268,14 +1233,14 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.proposeGuardian(friendAccount); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert(); openfortAccount.proposeGuardian(address(0)); // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1322,7 +1287,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1367,7 +1332,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1395,7 +1360,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1431,7 +1396,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1443,7 +1408,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { skip(1); vm.expectRevert(); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) @@ -1477,7 +1442,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1495,7 +1460,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectEmit(true, true, false, true); emit GuardianProposalCancelled(friendAccount); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.cancelGuardianProposal(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1503,7 +1468,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Friend account should not be a guardian yet assertEq(openfortAccount.isGuardian(friendAccount), false); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert(UnknownProposal.selector); openfortAccount.confirmGuardianProposal(friendAccount); @@ -1527,25 +1492,25 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect revert because the owner cannot be proposed as guardian vm.expectRevert(); - vm.prank(accountAdmin); - openfortAccount.proposeGuardian(accountAdmin); + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(openfortAdmin); // Verify that the number of guardians is still 1 (default) assertEq(openfortAccount.guardianCount(), 1); // Owner account should not be a guardian yet - assertEq(openfortAccount.isGuardian(accountAdmin), false); + assertEq(openfortAccount.isGuardian(openfortAdmin), false); // Expect revert because the default guardian cannot be proposed again vm.expectRevert(DuplicatedGuardian.selector); - vm.prank(accountAdmin); - openfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(openfortGuardian); // Verify that the number of guardians is still 1 (default) assertEq(openfortAccount.guardianCount(), 1); - // OPENFORT_GUARDIAN account should still be a guardian - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); + // openfortGuardian account should still be a guardian + assertEq(openfortAccount.isGuardian(openfortGuardian), true); } /* @@ -1572,7 +1537,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friends[index], block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friends[index]); } @@ -1615,7 +1580,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1634,13 +1599,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(beneficiary); // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); // Anyone can confirm a revocation. However, the security period has not passed yet @@ -1676,7 +1641,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1691,25 +1656,25 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Trying to revoke a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + openfortAccount.revokeGuardian(openfortGuardian); // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + emit GuardianRevocationRequested(openfortGuardian, block.timestamp + SECURITY_PERIOD); + vm.prank(openfortAdmin); + openfortAccount.revokeGuardian(openfortGuardian); // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Default account is not a guardian anymore - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + assertEq(openfortAccount.isGuardian(openfortGuardian), false); // Verify that the number of guardians is 1 again assertEq(openfortAccount.guardianCount(), 1); } @@ -1730,7 +1695,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1749,13 +1714,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(beneficiary); // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); // Anyone can confirm a revocation. However, the security period has not passed yet @@ -1774,21 +1739,21 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + emit GuardianRevocationRequested(openfortGuardian, block.timestamp + SECURITY_PERIOD); + vm.prank(openfortAdmin); + openfortAccount.revokeGuardian(openfortGuardian); // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Default account is not a guardian anymore - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + assertEq(openfortAccount.isGuardian(openfortGuardian), false); // Verify that the number of guardians is 1 again assertEq(openfortAccount.guardianCount(), 0); } @@ -1809,7 +1774,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1819,7 +1784,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); skip(1); @@ -1850,7 +1815,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1865,13 +1830,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) openfortAccount.revokeGuardian(friendAccount); vm.expectRevert(DuplicatedRevoke.selector); skip(1); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); skip(SECURITY_PERIOD); @@ -1894,32 +1859,32 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + emit GuardianRevocationRequested(openfortGuardian, block.timestamp + SECURITY_PERIOD); + vm.prank(openfortAdmin); // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + openfortAccount.revokeGuardian(openfortGuardian); skip(SECURITY_PERIOD + 1); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Verify that the number of guardians is now 0 assertEq(openfortAccount.guardianCount(), 0); // default (openfort) account should not be a guardian anymore - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + assertEq(openfortAccount.isGuardian(openfortGuardian), false); // Expect that we will see an event containing the default (openfort) account and security period vm.expectEmit(true, true, false, true); - emit GuardianProposed(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - openfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + emit GuardianProposed(openfortGuardian, block.timestamp + SECURITY_PERIOD); + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(openfortGuardian); skip(SECURITY_PERIOD + 1); - openfortAccount.confirmGuardianProposal(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianProposal(openfortGuardian); // Verify that the number of guardians is now 1 again assertEq(openfortAccount.guardianCount(), 1); // default (openfort) account should be a guardian again - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); + assertEq(openfortAccount.isGuardian(openfortGuardian), true); } /* @@ -1938,7 +1903,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1956,13 +1921,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(beneficiary); // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); // Anyone can confirm a revocation. However, the security period has not passed yet @@ -1976,7 +1941,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationCancelled(friendAccount); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.cancelGuardianRevocation(friendAccount); // Friend account is not a guardian anymore @@ -2001,10 +1966,10 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortFactory.updateInitialGuardian(newInitialGuardian); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.updateInitialGuardian(newInitialGuardian); - address newAccountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "newNewNew", true); + address newAccountAddress = openfortFactory.createAccountWithNonce(openfortAdmin, "newNewNew", true); IBaseRecoverableAccount(payable(newAccountAddress)).getGuardians(); } @@ -2025,7 +1990,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); // Adding and removing guardians - vm.startPrank(accountAdmin); + vm.startPrank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); openfortAccount.proposeGuardian(friendAccount2); vm.stopPrank(); @@ -2040,13 +2005,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(UnknownRevoke.selector); openfortAccount.confirmGuardianRevocation(friendAccount); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert(MustBeGuardian.selector); openfortAccount.revokeGuardian(friendAccount2); // Notice this tries to revoke a non-existent guardian! vm.expectRevert(DuplicatedGuardian.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Notice this tries to register a guardian AGAIN! - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); // Starting a valid revocation process skip(SECURITY_PERIOD + 1); // Try to confirm a guardian that is already valid and pending to revoke @@ -2065,13 +2030,13 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); vm.expectRevert(MustBeGuardian.selector); - openfortAccount.startRecovery(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(openfortGuardian); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); vm.expectRevert(GuardianCannotBeOwner.selector); - openfortAccount.startRecovery(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(openfortGuardian); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); @@ -2083,7 +2048,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { function testBasicChecksCompleteRecovery() public { ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); @@ -2113,7 +2078,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); @@ -2122,7 +2087,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, OPENFORT_GUARDIAN_PKEY); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, openfortGuardianKey); skip(RECOVERY_PERIOD + 1); openfortAccount.completeRecovery(signatures); @@ -2153,11 +2118,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount2); skip(1); @@ -2168,7 +2133,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { { // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); } @@ -2210,11 +2175,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount2); skip(1); @@ -2225,7 +2190,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { { // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); } @@ -2244,7 +2209,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // it should still be locked and the admin still be the same assertEq(openfortAccount.isLocked(), true); - assertEq(openfortAccount.owner(), accountAdmin); + assertEq(openfortAccount.owner(), openfortAdmin); } /* @@ -2271,7 +2236,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Adding and removing guardians { - vm.startPrank(accountAdmin); + vm.startPrank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); openfortAccount.proposeGuardian(friendAccount2); openfortAccount.proposeGuardian(friendAccount3); @@ -2284,12 +2249,12 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { openfortAccount.confirmGuardianProposal(friendAccount3); openfortAccount.confirmGuardianProposal(friendAccount4); - vm.prank(accountAdmin); - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + vm.prank(openfortAdmin); + openfortAccount.revokeGuardian(openfortGuardian); vm.expectRevert(PendingRevokeNotOver.selector); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); skip(SECURITY_PERIOD + 1); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); } // Start the recovery process @@ -2297,7 +2262,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Default Openfort guardian tries starts a recovery process because the owner lost the PK // It should not work as it is not a guardian anymore vm.expectRevert(MustBeGuardian.selector); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); vm.prank(friendAccount2); openfortAccount.startRecovery(address(beneficiary)); @@ -2341,11 +2306,11 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount2); skip(1); @@ -2356,14 +2321,14 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { { // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); } // notice: wrong new oner!!! bytes32 structHash = - keccak256(abi.encode(RECOVER_TYPEHASH, factoryAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); + keccak256(abi.encode(RECOVER_TYPEHASH, openfortAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); bytes[] memory signatures = new bytes[](2); signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address @@ -2375,7 +2340,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // it should still be locked and the admin still be the same assertEq(openfortAccount.isLocked(), true); - assertEq(openfortAccount.owner(), accountAdmin); + assertEq(openfortAccount.owner(), openfortAdmin); } /* @@ -2385,14 +2350,14 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.cancelRecovery(); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.cancelRecovery(); bytes32 structHash = keccak256( @@ -2400,14 +2365,14 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, OPENFORT_GUARDIAN_PKEY); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, openfortGuardianKey); skip(RECOVERY_PERIOD + 1); vm.expectRevert(NoOngoingRecovery.selector); openfortAccount.completeRecovery(signatures); assertEq(openfortAccount.isLocked(), false); - assertEq(openfortAccount.owner(), accountAdmin); + assertEq(openfortAccount.owner(), openfortAdmin); } /* @@ -2417,18 +2382,18 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); // Calling startRecovery() again should revert and have no effect vm.expectRevert(OngoingRecovery.selector); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); // The accounts should still be locked assertEq(openfortAccount.isLocked(), true); - assertEq(openfortAccount.owner(), accountAdmin); + assertEq(openfortAccount.owner(), openfortAdmin); } /** @@ -2448,7 +2413,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -2473,7 +2438,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.transferOwnership(newOwner); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.transferOwnership(newOwner); vm.prank(newOwner); @@ -2498,7 +2463,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Original owner registers a session key - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); // Using the session key @@ -2515,7 +2480,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { // Register a new owner address newOwner = makeAddr("newOwner"); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.transferOwnership(newOwner); vm.prank(newOwner); openfortAccount.acceptOwnership(); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index b8fe035..94da641 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -8,7 +8,7 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -import {EntryPoint, IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; +import {IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; import {IUpgradeableOpenfortAccount} from "contracts/interfaces/IUpgradeableOpenfortAccount.sol"; @@ -17,8 +17,8 @@ import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/Upgradeable import {UpgradeableOpenfortFactory} from "contracts/core/upgradeable/UpgradeableOpenfortFactory.sol"; import {UpgradeableOpenfortProxy} from "contracts/core/upgradeable/UpgradeableOpenfortProxy.sol"; import {MockV2UpgradeableOpenfortAccount} from "contracts/mock/MockV2UpgradeableOpenfortAccount.sol"; -import {OpenfortBaseTest, MockERC20, MockERC721, MockERC1155} from "../OpenfortBaseTest.t.sol"; -import {SimpleNFT} from "contracts/mock/SimpleNFT.sol"; +import {OpenfortBaseTest} from "../OpenfortBaseTest.t.sol"; +import {UpgradeableOpenfortDeploy} from "script/deployUpgradeableAccounts.s.sol"; contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { using ECDSA for bytes32; @@ -29,59 +29,20 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { /** * @notice Initialize the UpgradeableOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory - * - accountAdmin is the account used to deploy new upgradeable accounts + * - openfortAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory + * - openfortAdmin is the account used to deploy new upgradeable accounts * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps */ - function setUp() public { - versionSalt = bytes32(0x0); - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); - (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); - - vm.startPrank(factoryAdmin); - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at correct address) - else { - EntryPoint entryPointAux = new EntryPoint(); - bytes memory code = address(entryPointAux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } - // deploy upgradeable account implementation - vm.expectEmit(true, true, false, true); - emit AccountImplementationDeployed(factoryAdmin); - upgradeableOpenfortAccountImpl = new UpgradeableOpenfortAccount{salt: versionSalt}(); - // deploy upgradeable account factory - openfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( - factoryAdmin, - address(entryPoint), - address(upgradeableOpenfortAccountImpl), - RECOVERY_PERIOD, - SECURITY_PERIOD, - SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN - ); + function setUp() public override { + super.setUp(); + + UpgradeableOpenfortDeploy upgradeableOpenfortDeploy = new UpgradeableOpenfortDeploy(); + (upgradeableOpenfortAccountImpl, openfortFactory) = upgradeableOpenfortDeploy.run(); // Create an upgradeable account wallet and get its address - accountAddress = openfortFactory.createAccountWithNonce(accountAdmin, "1", true); - - // deploy a new TestCounter - testCounter = new TestCounter{salt: versionSalt}(); - // deploy a new MockERC20 (ERC20) - mockERC20 = new MockERC20{salt: versionSalt}(); - mockERC721 = new MockERC721{salt: versionSalt}(); - mockERC1155 = new MockERC1155{salt: versionSalt}(); - vm.stopPrank(); + vm.prank(openfortAdmin); + accountAddress = openfortFactory.createAccountWithNonce(openfortAdmin, "1", true); } /* @@ -92,29 +53,29 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { openfortFactory.addStake{value: 1 ether}(10); vm.expectRevert("no stake specified"); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.addStake(10); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.addStake{value: 1 ether}(10); vm.expectRevert("Ownable: caller is not the owner"); openfortFactory.unlockStake(); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.unlockStake(); vm.expectRevert("Ownable: caller is not the owner"); - openfortFactory.withdrawStake(payable(factoryAdmin)); + openfortFactory.withdrawStake(payable(openfortAdmin)); vm.expectRevert("Stake withdrawal is not due"); - vm.prank(factoryAdmin); - openfortFactory.withdrawStake(payable(factoryAdmin)); + vm.prank(openfortAdmin); + openfortFactory.withdrawStake(payable(openfortAdmin)); skip(11); - vm.prank(factoryAdmin); - openfortFactory.withdrawStake(payable(factoryAdmin)); + vm.prank(openfortAdmin); + openfortFactory.withdrawStake(payable(openfortAdmin)); } /* @@ -124,13 +85,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(openfortFactory.implementation(), address(upgradeableOpenfortAccountImpl)); vm.expectRevert("Initializable: contract is already initialized"); upgradeableOpenfortAccountImpl.initialize( - accountAdmin, + openfortAdmin, address(entryPoint), RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, - OPENFORT_GUARDIAN + openfortGuardian ); } @@ -139,24 +100,24 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testCreateAccountWithNonceViaFactory() public { // Get the counterfactual address - vm.prank(factoryAdmin); - address accountAddress2 = openfortFactory.getAddressWithNonce(accountAdmin, "2"); + vm.prank(openfortAdmin); + address accountAddress2 = openfortFactory.getAddressWithNonce(openfortAdmin, "2"); // Expect that we will see an event containing the account and admin vm.expectEmit(true, true, false, true); - emit AccountCreated(accountAddress2, accountAdmin); + emit AccountCreated(accountAddress2, openfortAdmin); // Deploy a upgradeable account to the counterfactual address - vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2", true); + vm.prank(openfortAdmin); + openfortFactory.createAccountWithNonce(openfortAdmin, "2", true); // Calling it again should just return the address and not create another account - vm.prank(factoryAdmin); - openfortFactory.createAccountWithNonce(accountAdmin, "2", true); + vm.prank(openfortAdmin); + openfortFactory.createAccountWithNonce(openfortAdmin, "2", true); // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); - assertEq(accountAddress2, openfortFactory.getAddressWithNonce(accountAdmin, "2")); + vm.prank(openfortAdmin); + assertEq(accountAddress2, openfortFactory.getAddressWithNonce(openfortAdmin, "2")); } /* @@ -164,27 +125,27 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { */ function testFuzzCreateAccountWithNonceViaFactory(address _adminAddress, bytes32 _nonce) public { // Get the counterfactual address - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); address accountAddress2 = openfortFactory.getAddressWithNonce(_adminAddress, _nonce); // Expect that we will see an event containing the account and admin if (_adminAddress == address(0)) { vm.expectRevert(); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); } else { vm.expectEmit(true, true, false, true); emit AccountCreated(accountAddress2, _adminAddress); // Deploy a upgradeable account to the counterfactual address - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); // Calling it again should just return the address and not create another account - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortFactory.createAccountWithNonce(_adminAddress, _nonce, true); // Make sure the counterfactual address has not been altered - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); assertEq(accountAddress2, openfortFactory.getAddressWithNonce(_adminAddress, _nonce)); } } @@ -199,19 +160,19 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // revert(); // Make sure the smart account does not have any code yet - address account2 = openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2")); + address account2 = openfortFactory.getAddressWithNonce(openfortAdmin, bytes32("2")); assertEq(account2.code.length, 0); bytes memory initCallData = - abi.encodeWithSignature("createAccountWithNonce(address,bytes32,bool)", accountAdmin, bytes32("2"), true); + abi.encodeWithSignature("createAccountWithNonce(address,bytes32,bool)", openfortAdmin, bytes32("2"), true); bytes memory initCode = abi.encodePacked(abi.encodePacked(address(openfortFactory)), initCallData); UserOperation[] memory userOpCreateAccount = - _setupUserOpExecute(account2, accountAdminPKey, initCode, address(0), 0, bytes("")); + _setupUserOpExecute(account2, openfortAdminPKey, initCode, address(0), 0, bytes("")); // Expect that we will see an event containing the account and admin vm.expectEmit(true, true, false, true); - emit AccountCreated(account2, accountAdmin); + emit AccountCreated(account2, openfortAdmin); entryPoint.handleOps(userOpCreateAccount, beneficiary); @@ -219,7 +180,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assert(account2.code.length > 0); // Make sure the counterfactual address has not been altered - assertEq(account2, openfortFactory.getAddressWithNonce(accountAdmin, bytes32("2"))); + assertEq(account2, openfortFactory.getAddressWithNonce(openfortAdmin, bytes32("2"))); } /* @@ -230,7 +191,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).execute( address(testCounter), 0, abi.encodeWithSignature("count()") ); @@ -248,7 +209,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, openfortAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); entryPoint.depositTo{value: 1 ether}(accountAddress); @@ -280,7 +241,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(accountAddress, accountAdminPKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, openfortAdminPKey, bytes(""), targets, values, callData); entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); @@ -310,7 +271,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { } UserOperation[] memory userOp = - _setupUserOpExecuteBatch(accountAddress, accountAdminPKey, bytes(""), targets, values, callData); + _setupUserOpExecuteBatch(accountAddress, openfortAdminPKey, bytes(""), targets, values, callData); entryPoint.depositTo{value: 1 ether}(accountAddress); vm.expectRevert(); @@ -357,7 +318,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey( sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist ); @@ -390,7 +351,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOp( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", @@ -439,7 +400,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOp( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), abi.encodeWithSignature( "registerSessionKey(address,uint48,uint48,uint48,address[])", @@ -509,7 +470,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), accountAddress, 0, @@ -575,7 +536,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory emptyWhitelist; vm.warp(100); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -604,9 +565,9 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).revokeSessionKey(sessionKey); UserOperation[] memory userOp = _setupUserOpExecute( @@ -636,7 +597,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // We are now in block 100, but our session key is valid until block 150 vm.warp(100); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 150, 1, emptyWhitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -675,7 +636,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // We are now in block 100, but our session key is valid until block 150 vm.warp(100); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 150, 2, emptyWhitelist); uint256 count = 3; @@ -713,7 +674,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 0, 100, emptyWhitelist); vm.prank(beneficiary); IBaseRecoverableAccount(payable(accountAddress)).revokeSessionKey(sessionKey); @@ -744,7 +705,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -772,7 +733,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory whitelist = new address[](11); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -801,7 +762,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(testCounter); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); // Verify that the registered key is not a MasterKey but has whitelisting @@ -847,7 +808,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(accountAddress); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); UserOperation[] memory userOp = _setupUserOpExecute( @@ -876,7 +837,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { address[] memory whitelist = new address[](1); whitelist[0] = address(accountAddress); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 1, whitelist); uint256 count = 3; @@ -925,14 +886,14 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { whitelist[i] = address(testCounter); } vm.expectRevert("Whitelist too big"); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); UpgradeableOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 3, whitelist); } /* * Change the owner of an account and call TestCounter directly. * Important use-case: - * 1- accountAdmin is Openfort's master wallet and is managing the account of the user. + * 1- openfortAdmin is Openfort's master wallet and is managing the account of the user. * 2- The user claims the ownership of the account to Openfort so Openfort calls * transferOwnership() to the account. * 3- The user has to "officially" claim the ownership of the account by directly @@ -941,23 +902,23 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * 5- Test that the new owner can directly interact with the account and make it call the testCounter contract. */ function testChangeOwnershipAndCountDirect() public { - address accountAdmin2; - uint256 accountAdmin2PKey; - (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); + address openfortAdmin2; + uint256 openfortAdmin2PKey; + (openfortAdmin2, openfortAdmin2PKey) = makeAddrAndKey("openfortAdmin2"); vm.expectRevert("Ownable: caller is not the owner"); - IBaseRecoverableAccount(payable(accountAddress)).transferOwnership(accountAdmin2); + IBaseRecoverableAccount(payable(accountAddress)).transferOwnership(openfortAdmin2); - vm.prank(accountAdmin); - IBaseRecoverableAccount(payable(accountAddress)).transferOwnership(accountAdmin2); - vm.prank(accountAdmin2); + vm.prank(openfortAdmin); + IBaseRecoverableAccount(payable(accountAddress)).transferOwnership(openfortAdmin2); + vm.prank(openfortAdmin2); IBaseRecoverableAccount(payable(accountAddress)).acceptOwnership(); // Verify that the counter is still set to 0 assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the upgradeable account wallet (deployer) call "count" - vm.prank(accountAdmin2); + vm.prank(openfortAdmin2); IBaseRecoverableAccount(payable(accountAddress)).execute( address(testCounter), 0, abi.encodeWithSignature("count()") ); @@ -970,20 +931,20 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Change the owner of an account and call TestCounter though the Entrypoint */ function testChangeOwnershipAndCountEntryPoint() public { - address accountAdmin2; - uint256 accountAdmin2PKey; - (accountAdmin2, accountAdmin2PKey) = makeAddrAndKey("accountAdmin2"); + address openfortAdmin2; + uint256 openfortAdmin2PKey; + (openfortAdmin2, openfortAdmin2PKey) = makeAddrAndKey("openfortAdmin2"); - vm.prank(accountAdmin); - IBaseRecoverableAccount(payable(accountAddress)).transferOwnership(accountAdmin2); - vm.prank(accountAdmin2); + vm.prank(openfortAdmin); + IBaseRecoverableAccount(payable(accountAddress)).transferOwnership(openfortAdmin2); + vm.prank(openfortAdmin2); IBaseRecoverableAccount(payable(accountAddress)).acceptOwnership(); // Verify that the counter is still set to 0 assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - accountAddress, accountAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, openfortAdmin2PKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); entryPoint.depositTo{value: 1 ether}(accountAddress); @@ -1008,7 +969,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -1032,7 +993,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the account wallet call "count" - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).execute( address(testCounter), 0, abi.encodeWithSignature("count()") ); @@ -1042,7 +1003,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Make the admin of the account wallet call "execute" which call count vm.expectRevert(OpenfortErrorsAndEvents.NotOwnerOrEntrypoint.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).execute( address(accountAddress), 0, @@ -1060,7 +1021,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); // Make the admin of the account wallet call "count" - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).execute( address(testCounter), 0, abi.encodeWithSignature("count()") ); @@ -1083,7 +1044,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Make the admin of the account wallet call "execute" which call count vm.expectRevert(OpenfortErrorsAndEvents.NotOwnerOrEntrypoint.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IBaseRecoverableAccount(payable(accountAddress)).executeBatch(targets, values, callData); } @@ -1093,7 +1054,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testReceiveNativeToken() public { assertEq(address(accountAddress).balance, 0); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); (bool success,) = payable(accountAddress).call{value: 1000}(""); assert(success); assertEq(address(accountAddress).balance, 1000); @@ -1106,16 +1067,16 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { uint256 value = 1000; assertEq(address(accountAddress).balance, 0); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); (bool success,) = payable(accountAddress).call{value: value}(""); assertEq(address(accountAddress).balance, value); assert(success); assertEq(beneficiary.balance, 0); UserOperation[] memory userOp = - _setupUserOpExecute(accountAddress, accountAdminPKey, bytes(""), address(beneficiary), value, bytes("")); + _setupUserOpExecute(accountAddress, openfortAdminPKey, bytes(""), address(beneficiary), value, bytes("")); - EntryPoint(entryPoint).handleOps(userOp, beneficiary); + entryPoint.handleOps(userOp, beneficiary); assertEq(beneficiary.balance, value); } @@ -1127,7 +1088,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), 0); UserOperation[] memory userOp = _setupUserOpExecute( - accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") + accountAddress, openfortAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") ); entryPoint.depositTo{value: 1 ether}(accountAddress); @@ -1143,7 +1104,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { * Create an account and upgrade its implementation */ function testUpgradeAccount() public { - assertEq(IBaseRecoverableAccount(payable(accountAddress)).owner(), address(accountAdmin)); + assertEq(IBaseRecoverableAccount(payable(accountAddress)).owner(), address(openfortAdmin)); assertEq(address(IBaseRecoverableAccount(payable(accountAddress)).entryPoint()), address(entryPoint)); MockV2UpgradeableOpenfortAccount newAccountImplementation = new MockV2UpgradeableOpenfortAccount(); UpgradeableOpenfortProxy p = UpgradeableOpenfortProxy(payable(accountAddress)); @@ -1157,7 +1118,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(); MockV2UpgradeableOpenfortAccount(payable(accountAddress)).easterEgg(); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); UpgradeableOpenfortAccount(payable(accountAddress)).upgradeTo(address(newAccountImplementation)); // Notice that, even though we bind the address to the old implementation, entryPoint() is now changed @@ -1169,7 +1130,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(MockV2UpgradeableOpenfortAccount(payable(accountAddress)).easterEgg(), 42); vm.expectRevert("disabled!"); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); IUpgradeableOpenfortAccount(payable(accountAddress)).updateEntryPoint(beneficiary); // Printing account address and the implementation address. Impl address should have changed @@ -1187,30 +1148,30 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.etch(oldEntryPoint, code); // Simulate there's code here address newEntryPoint = vm.envAddress("ENTRY_POINT_ADDRESS"); UpgradeableOpenfortFactory openfortFactoryOld = new UpgradeableOpenfortFactory{salt: versionSalt}( - factoryAdmin, + openfortAdmin, oldEntryPoint, address(upgradeableOpenfortAccountImpl), RECOVERY_PERIOD, SECURITY_PERIOD, SECURITY_WINDOW, LOCK_PERIOD, - OPENFORT_GUARDIAN + openfortGuardian ); // Create an upgradeable account wallet using the old EntryPoint and get its address address payable oldAccountAddress = - payable(openfortFactoryOld.createAccountWithNonce(accountAdmin, "999", true)); + payable(openfortFactoryOld.createAccountWithNonce(openfortAdmin, "999", true)); IUpgradeableOpenfortAccount upgradeableAccount = IUpgradeableOpenfortAccount(oldAccountAddress); assertEq(address(upgradeableAccount.entryPoint()), oldEntryPoint); vm.expectRevert("Ownable: caller is not the owner"); upgradeableAccount.updateEntryPoint(newEntryPoint); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert(OpenfortErrorsAndEvents.NotAContract.selector); upgradeableAccount.updateEntryPoint(address(0)); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); upgradeableAccount.updateEntryPoint(newEntryPoint); assertEq(address(upgradeableAccount.entryPoint()), newEntryPoint); @@ -1218,13 +1179,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testIsValidSignature() public { bytes32 hash = keccak256("Signed by Owner"); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, hash); address signer = ecrecover(hash, v, r, s); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes memory signature = abi.encodePacked(r, s, v); signer = ECDSA.recover(hash, signature); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! @@ -1234,13 +1195,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testIsValidSignatureMessage() public { bytes32 hash = keccak256("Signed by Owner"); bytes32 hashMessage = hash.toEthSignedMessageHash(); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, hashMessage); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, hashMessage); address signer = ecrecover(hashMessage, v, r, s); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes memory signature = abi.encodePacked(r, s, v); signer = ECDSA.recover(hashMessage, signature); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS! @@ -1279,11 +1240,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) ); - bytes memory signature = getEIP712SignatureFrom(accountAddress, structHash, accountAdminPKey); + bytes memory signature = getEIP712SignatureFrom(accountAddress, structHash, openfortAdminPKey); bytes32 hash712 = domainSeparator.toTypedDataHash(structHash); address signer = hash712.recover(signature); - assertEq(accountAdmin, signer); // [PASS] + assertEq(openfortAdmin, signer); // [PASS] bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); assertNotEq(valid, bytes4(0xffffffff)); // SHOULD PASS @@ -1302,7 +1263,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Make the admin of the upgradeable account wallet (deployer) call "depositTo()" vm.expectEmit(true, true, false, true); emit Deposited(accountAddress, 1 ether); // Notice 2 ether is the current deposit, not the deposited - vm.prank(accountAdmin); + vm.prank(openfortAdmin); account.execute{value: 1 ether}( address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) ); @@ -1315,13 +1276,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectEmit(true, true, false, true); emit Deposited(accountAddress, 1 ether); // Make the admin of the upgradeable account wallet (deployer) call "depositTo()" - vm.prank(accountAdmin); + vm.prank(openfortAdmin); account.execute{value: 1 ether}( address(entryPoint), 1 ether, abi.encodeWithSignature("depositTo(address)", accountAddress) ); assertEq(1 ether, account.getDeposit()); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); account.execute{value: 1 ether}( address(entryPoint), 0, @@ -1347,14 +1308,14 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(MustBeGuardian.selector); openfortAccount.lock(); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.lock(); assertEq(openfortAccount.isLocked(), true); assertEq(openfortAccount.getLock(), block.timestamp + LOCK_PERIOD); vm.expectRevert(AccountLocked.selector); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.lock(); // Automatically unlock @@ -1375,7 +1336,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(MustBeGuardian.selector); openfortAccount.lock(); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.lock(); assertEq(openfortAccount.isLocked(), true); @@ -1387,14 +1348,14 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { openfortAccount.unlock(); assertEq(openfortAccount.isLocked(), true); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.unlock(); assertEq(openfortAccount.isLocked(), false); assertEq(openfortAccount.getLock(), 0); vm.expectRevert(AccountNotLocked.selector); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.unlock(); } @@ -1424,7 +1385,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1471,7 +1432,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1516,7 +1477,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1544,7 +1505,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1580,7 +1541,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1592,7 +1553,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { skip(1); vm.expectRevert(); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) @@ -1626,7 +1587,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1644,7 +1605,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectEmit(true, true, false, true); emit GuardianProposalCancelled(friendAccount); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.cancelGuardianProposal(friendAccount); // Verify that the number of guardians is still 1 (default) @@ -1652,7 +1613,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Friend account should not be a guardian yet assertEq(openfortAccount.isGuardian(friendAccount), false); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert(UnknownProposal.selector); openfortAccount.confirmGuardianProposal(friendAccount); @@ -1676,25 +1637,25 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect revert because the owner cannot be proposed as guardian vm.expectRevert(); - vm.prank(accountAdmin); - openfortAccount.proposeGuardian(accountAdmin); + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(openfortAdmin); // Verify that the number of guardians is still 1 (default) assertEq(openfortAccount.guardianCount(), 1); // Owner account should not be a guardian yet - assertEq(openfortAccount.isGuardian(accountAdmin), false); + assertEq(openfortAccount.isGuardian(openfortAdmin), false); // Expect revert because the default guardian cannot be proposed again vm.expectRevert(DuplicatedGuardian.selector); - vm.prank(accountAdmin); - openfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(openfortGuardian); // Verify that the number of guardians is still 1 (default) assertEq(openfortAccount.guardianCount(), 1); - // OPENFORT_GUARDIAN account should still be a guardian - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); + // openfortGuardian account should still be a guardian + assertEq(openfortAccount.isGuardian(openfortGuardian), true); } /* @@ -1721,7 +1682,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friends[index], block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friends[index]); } @@ -1764,7 +1725,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1783,13 +1744,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(beneficiary); // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); // Anyone can confirm a revocation. However, the security period has not passed yet @@ -1825,7 +1786,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1840,25 +1801,25 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Trying to revoke a guardian not using the owner vm.expectRevert("Ownable: caller is not the owner"); - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + openfortAccount.revokeGuardian(openfortGuardian); // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + emit GuardianRevocationRequested(openfortGuardian, block.timestamp + SECURITY_PERIOD); + vm.prank(openfortAdmin); + openfortAccount.revokeGuardian(openfortGuardian); // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Default account is not a guardian anymore - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + assertEq(openfortAccount.isGuardian(openfortGuardian), false); // Verify that the number of guardians is 1 again assertEq(openfortAccount.guardianCount(), 1); } @@ -1879,7 +1840,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1898,13 +1859,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(beneficiary); // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); // Anyone can confirm a revocation. However, the security period has not passed yet @@ -1923,21 +1884,21 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + emit GuardianRevocationRequested(openfortGuardian, block.timestamp + SECURITY_PERIOD); + vm.prank(openfortAdmin); + openfortAccount.revokeGuardian(openfortGuardian); // Anyone can confirm a revocation. However, the security period has not passed yet skip(1); vm.expectRevert(PendingRevokeNotOver.selector); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Anyone can confirm a revocation after security period skip(SECURITY_PERIOD); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Default account is not a guardian anymore - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + assertEq(openfortAccount.isGuardian(openfortGuardian), false); // Verify that the number of guardians is 1 again assertEq(openfortAccount.guardianCount(), 0); } @@ -1958,7 +1919,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -1968,7 +1929,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); skip(1); @@ -1999,7 +1960,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -2014,13 +1975,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) openfortAccount.revokeGuardian(friendAccount); vm.expectRevert(DuplicatedRevoke.selector); skip(1); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); skip(SECURITY_PERIOD); @@ -2043,32 +2004,32 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); - emit GuardianRevocationRequested(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + emit GuardianRevocationRequested(openfortGuardian, block.timestamp + SECURITY_PERIOD); + vm.prank(openfortAdmin); // Now let's check that, even after the revert, it is possible to confirm the proposal (no DoS) - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + openfortAccount.revokeGuardian(openfortGuardian); skip(SECURITY_PERIOD + 1); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); // Verify that the number of guardians is now 0 assertEq(openfortAccount.guardianCount(), 0); // default (openfort) account should not be a guardian anymore - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), false); + assertEq(openfortAccount.isGuardian(openfortGuardian), false); // Expect that we will see an event containing the default (openfort) account and security period vm.expectEmit(true, true, false, true); - emit GuardianProposed(OPENFORT_GUARDIAN, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); - openfortAccount.proposeGuardian(OPENFORT_GUARDIAN); + emit GuardianProposed(openfortGuardian, block.timestamp + SECURITY_PERIOD); + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(openfortGuardian); skip(SECURITY_PERIOD + 1); - openfortAccount.confirmGuardianProposal(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianProposal(openfortGuardian); // Verify that the number of guardians is now 1 again assertEq(openfortAccount.guardianCount(), 1); // default (openfort) account should be a guardian again - assertEq(openfortAccount.isGuardian(OPENFORT_GUARDIAN), true); + assertEq(openfortAccount.isGuardian(openfortGuardian), true); } /* @@ -2087,7 +2048,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -2105,13 +2066,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Trying to revoke a non-existent guardian (random beneficiary address) vm.expectRevert(MustBeGuardian.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(beneficiary); // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationRequested(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); // Anyone can confirm a revocation. However, the security period has not passed yet @@ -2125,7 +2086,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianRevocationCancelled(friendAccount); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.cancelGuardianRevocation(friendAccount); // Friend account is not a guardian anymore @@ -2155,7 +2116,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { (friendAccount2, friendAccount2PK) = makeAddrAndKey("friend2"); // Adding and removing guardians - vm.startPrank(accountAdmin); + vm.startPrank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); openfortAccount.proposeGuardian(friendAccount2); vm.stopPrank(); @@ -2170,13 +2131,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(UnknownRevoke.selector); openfortAccount.confirmGuardianRevocation(friendAccount); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); vm.expectRevert(MustBeGuardian.selector); openfortAccount.revokeGuardian(friendAccount2); // Notice this tries to revoke a non-existent guardian! vm.expectRevert(DuplicatedGuardian.selector); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); // Notice this tries to register a guardian AGAIN! - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.revokeGuardian(friendAccount); // Starting a valid revocation process skip(SECURITY_PERIOD + 1); @@ -2196,13 +2157,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); vm.expectRevert(MustBeGuardian.selector); - openfortAccount.startRecovery(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(openfortGuardian); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); vm.expectRevert(GuardianCannotBeOwner.selector); - openfortAccount.startRecovery(OPENFORT_GUARDIAN); + openfortAccount.startRecovery(openfortGuardian); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); @@ -2214,7 +2175,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testBasicChecksCompleteRecovery() public { IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); @@ -2244,7 +2205,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); @@ -2253,7 +2214,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, OPENFORT_GUARDIAN_PKEY); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, openfortGuardianKey); skip(RECOVERY_PERIOD + 1); openfortAccount.completeRecovery(signatures); @@ -2284,11 +2245,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount2); skip(1); @@ -2299,7 +2260,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { { // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); } @@ -2342,11 +2303,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount2); skip(1); @@ -2357,7 +2318,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { { // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); } @@ -2376,7 +2337,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // it should still be locked and the admin still be the same assertEq(openfortAccount.isLocked(), true); - assertEq(openfortAccount.owner(), accountAdmin); + assertEq(openfortAccount.owner(), openfortAdmin); } /* @@ -2403,7 +2364,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Adding and removing guardians { - vm.startPrank(accountAdmin); + vm.startPrank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); openfortAccount.proposeGuardian(friendAccount2); openfortAccount.proposeGuardian(friendAccount3); @@ -2416,12 +2377,12 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { openfortAccount.confirmGuardianProposal(friendAccount3); openfortAccount.confirmGuardianProposal(friendAccount4); - vm.prank(accountAdmin); - openfortAccount.revokeGuardian(OPENFORT_GUARDIAN); + vm.prank(openfortAdmin); + openfortAccount.revokeGuardian(openfortGuardian); vm.expectRevert(PendingRevokeNotOver.selector); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); skip(SECURITY_PERIOD + 1); - openfortAccount.confirmGuardianRevocation(OPENFORT_GUARDIAN); + openfortAccount.confirmGuardianRevocation(openfortGuardian); } // Start the recovery process @@ -2429,7 +2390,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Default Openfort guardian tries starts a recovery process because the owner lost the PK // It should not work as it is not a guardian anymore vm.expectRevert(MustBeGuardian.selector); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); vm.prank(friendAccount2); openfortAccount.startRecovery(address(beneficiary)); @@ -2473,11 +2434,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount2, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount2); skip(1); @@ -2488,14 +2449,14 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { { // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); } // notice: wrong new oner!!! bytes32 structHash = - keccak256(abi.encode(RECOVER_TYPEHASH, factoryAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); + keccak256(abi.encode(RECOVER_TYPEHASH, openfortAdmin, uint64(block.timestamp + RECOVERY_PERIOD), uint32(2))); bytes[] memory signatures = new bytes[](2); signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, friendAccount2PK); // Using friendAccount2 first because it has a lower address @@ -2507,7 +2468,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // it should still be locked and the admin still be the same assertEq(openfortAccount.isLocked(), true); - assertEq(openfortAccount.owner(), accountAdmin); + assertEq(openfortAccount.owner(), openfortAdmin); } /* @@ -2517,14 +2478,14 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.cancelRecovery(); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.cancelRecovery(); bytes32 structHash = keccak256( @@ -2532,14 +2493,14 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { ); bytes[] memory signatures = new bytes[](1); - signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, OPENFORT_GUARDIAN_PKEY); + signatures[0] = getEIP712SignatureFrom(accountAddress, structHash, openfortGuardianKey); skip(RECOVERY_PERIOD + 1); vm.expectRevert(NoOngoingRecovery.selector); openfortAccount.completeRecovery(signatures); assertEq(openfortAccount.isLocked(), false); - assertEq(openfortAccount.owner(), accountAdmin); + assertEq(openfortAccount.owner(), openfortAdmin); } /* @@ -2549,18 +2510,18 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Default Openfort guardian starts a recovery process because the owner lost the PK - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); assertEq(openfortAccount.isLocked(), true); // Calling startRecovery() again should revert and have no effect vm.expectRevert(OngoingRecovery.selector); - vm.prank(OPENFORT_GUARDIAN); + vm.prank(openfortGuardian); openfortAccount.startRecovery(address(beneficiary)); // The accounts should still be locked assertEq(openfortAccount.isLocked(), true); - assertEq(openfortAccount.owner(), accountAdmin); + assertEq(openfortAccount.owner(), openfortAdmin); } /** @@ -2580,7 +2541,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Expect that we will see an event containing the friend account and security period vm.expectEmit(true, true, false, true); emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.proposeGuardian(friendAccount); skip(1); @@ -2605,7 +2566,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Ownable: caller is not the owner"); openfortAccount.transferOwnership(newOwner); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.transferOwnership(newOwner); vm.prank(newOwner); @@ -2630,7 +2591,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); // Original owner registers a session key - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); // Using the session key @@ -2647,7 +2608,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Register a new owner address newOwner = makeAddr("newOwner"); - vm.prank(accountAdmin); + vm.prank(openfortAdmin); openfortAccount.transferOwnership(newOwner); vm.prank(newOwner); openfortAccount.acceptOwnership(); @@ -2677,11 +2638,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { // Verify that the totalSupply is still 0 assertEq(mockERC20.totalSupply(), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", true); + address accountAddress2 = openfortFactory.createAccountWithNonce(openfortAdmin, "2", true); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -2703,7 +2664,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -2728,11 +2689,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testTransferERC721BetweenAccounts() public { assertEq(mockERC721.balanceOf(accountAddress), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", true); + address accountAddress2 = openfortFactory.createAccountWithNonce(openfortAdmin, "2", true); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC721), 0, @@ -2754,7 +2715,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC721), 0, @@ -2771,7 +2732,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { userOp = _setupUserOpExecute( accountAddress2, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC721), 0, @@ -2793,11 +2754,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testTransferERC1155BetweenAccounts() public { assertEq(mockERC1155.balanceOf(accountAddress, 1), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", true); + address accountAddress2 = openfortFactory.createAccountWithNonce(openfortAdmin, "2", true); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC1155), 0, @@ -2819,7 +2780,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC1155), 0, @@ -2843,11 +2804,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { function testTransferERC1155BetweenAccountsBatch() public { assertEq(mockERC1155.balanceOf(accountAddress, 1), 0); - address accountAddress2 = openfortFactory.createAccountWithNonce(accountAdmin, "2", true); + address accountAddress2 = openfortFactory.createAccountWithNonce(openfortAdmin, "2", true); - mockERC1155.mint(accountAdmin, 1, 2); + mockERC1155.mint(openfortAdmin, 1, 2); - assertEq(mockERC1155.balanceOf(accountAdmin, 1), 2); + assertEq(mockERC1155.balanceOf(openfortAdmin, 1), 2); assertEq(mockERC1155.balanceOf(accountAddress2, 1), 0); uint256[] memory ids = new uint256[](2); @@ -2857,23 +2818,13 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { amounts[0] = 1; amounts[1] = 1; - vm.prank(accountAdmin); - mockERC1155.safeBatchTransferFrom(accountAdmin, accountAddress2, ids, amounts, ""); + vm.prank(openfortAdmin); + mockERC1155.safeBatchTransferFrom(openfortAdmin, accountAddress2, ids, amounts, ""); - assertEq(mockERC1155.balanceOf(accountAdmin, 1), 0); + assertEq(mockERC1155.balanceOf(openfortAdmin, 1), 0); assertEq(mockERC1155.balanceOf(accountAddress2, 1), 2); } - /* - * Test for coverage purposes. - * SimpleNFT is an NFT contract used by Openfort in some internal tests - */ - function testSimpleNFT() public { - SimpleNFT simpleNFT = new SimpleNFT(); - simpleNFT.mint(accountAddress); - assertEq(simpleNFT.balanceOf(accountAddress), 1); - } - function testSupportsInterface() public { IBaseRecoverableAccount account = IBaseRecoverableAccount(payable(accountAddress)); assertTrue(account.supportsInterface(type(IERC721Receiver).interfaceId)); @@ -2887,11 +2838,11 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert(); openfortFactory.updateInitialGuardian(vm.addr(2)); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); vm.expectRevert(); openfortFactory.updateInitialGuardian(address(0)); - vm.prank(factoryAdmin); - openfortFactory.updateInitialGuardian(address(accountAdmin)); + vm.prank(openfortAdmin); + openfortFactory.updateInitialGuardian(address(openfortAdmin)); } } diff --git a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol index 6e13ffa..1fd7912 100644 --- a/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol +++ b/test/foundry/paymaster/OpenfortPaymasterV2Test.t.sol @@ -12,6 +12,7 @@ import {UpgradeableOpenfortAccount} from "contracts/core/upgradeable/Upgradeable import {OpenfortPaymasterV2} from "contracts/paymaster/OpenfortPaymasterV2.sol"; import {OpenfortBaseTest} from "../core/OpenfortBaseTest.t.sol"; import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; +import {UpgradeableOpenfortDeploy} from "script/deployUpgradeableAccounts.s.sol"; contract OpenfortPaymasterV2Test is OpenfortBaseTest { using ECDSA for bytes32; @@ -21,8 +22,8 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { address private paymasterAdmin; uint256 private paymasterAdminPKey; - UpgradeableOpenfortAccount public upgradeableOpenfortAccount; - UpgradeableOpenfortFactory public upgradeableOpenfortFactory; + UpgradeableOpenfortAccount public upgradeableOpenfortAccountImpl; + UpgradeableOpenfortFactory public openfortFactory; uint48 internal constant VALIDUNTIL = 2 ** 48 - 1; uint48 internal constant VALIDAFTER = 0; @@ -60,7 +61,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { }); // Sign UserOp - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(op); + bytes32 opHash = entryPoint.getUserOpHash(op); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); @@ -153,35 +154,17 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { /** * @notice Initialize the UpgradeableOpenfortAccount testing contract. * Scenario: - * - factoryAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory + * - openfortAdmin is the deployer (and owner) of the UpgradeableOpenfortFactory * - paymasterAdmin is the deployer (and owner) of the OpenfortPaymaster - * - accountAdmin is the account used to deploy new static accounts + * - openfortAdmin is the account used to deploy new static accounts * - entryPoint is the singleton EntryPoint * - testCounter is the counter used to test userOps */ - function setUp() public { - versionSalt = bytes32(0x0); - // Setup and fund signers - (factoryAdmin, factoryAdminPKey) = makeAddrAndKey("factoryAdmin"); - vm.deal(factoryAdmin, 100 ether); - (accountAdmin, accountAdminPKey) = makeAddrAndKey("accountAdmin"); - vm.deal(accountAdmin, 100 ether); + function setUp() public override { + super.setUp(); (paymasterAdmin, paymasterAdminPKey) = makeAddrAndKey("paymasterAdmin"); vm.deal(paymasterAdmin, 100 ether); - (OPENFORT_GUARDIAN, OPENFORT_GUARDIAN_PKEY) = makeAddrAndKey("OPENFORT_GUARDIAN"); - // If we are in a fork - if (vm.envAddress("ENTRY_POINT_ADDRESS").code.length > 0) { - entryPoint = EntryPoint(payable(vm.envAddress("ENTRY_POINT_ADDRESS"))); - } - // If not a fork, deploy entryPoint (at correct address) - else { - EntryPoint entryPoint_aux = new EntryPoint(); - bytes memory code = address(entryPoint_aux).code; - address targetAddr = address(vm.envAddress("ENTRY_POINT_ADDRESS")); - vm.etch(targetAddr, code); - entryPoint = EntryPoint(payable(targetAddr)); - } vm.prank(paymasterAdmin); openfortPaymaster = new OpenfortPaymasterV2{salt: versionSalt}(IEntryPoint(payable(address(entryPoint))), paymasterAdmin); @@ -193,28 +176,17 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { openfortPaymaster.addStake{value: 25 ether}(1); // deploy account factory - vm.prank(factoryAdmin); - upgradeableOpenfortAccount = new UpgradeableOpenfortAccount{salt: versionSalt}(); - vm.prank(factoryAdmin); - upgradeableOpenfortFactory = new UpgradeableOpenfortFactory{salt: versionSalt}( - factoryAdmin, - address(entryPoint), - address(upgradeableOpenfortAccount), - RECOVERY_PERIOD, - SECURITY_PERIOD, - SECURITY_WINDOW, - LOCK_PERIOD, - OPENFORT_GUARDIAN - ); + UpgradeableOpenfortDeploy upgradeableOpenfortDeploy = new UpgradeableOpenfortDeploy(); + (upgradeableOpenfortAccountImpl, openfortFactory) = upgradeableOpenfortDeploy.run(); + // deploy a new TestCounter testCounter = new TestCounter(); - // deploy a new MockERC20 and mint 1000 - mockERC20 = new MockERC20(); + // mint 1000 mockERC20 mockERC20.mint(address(this), 1_000 * 10 ** 18); // Create an Openfort account and get its address - vm.prank(factoryAdmin); - accountAddress = upgradeableOpenfortFactory.createAccountWithNonce(accountAdmin, "1", true); + vm.prank(openfortAdmin); + accountAddress = openfortFactory.createAccountWithNonce(openfortAdmin, "1", true); } /* @@ -230,7 +202,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { * Deposit should fail if not the owner */ function testFailDeposit() public { - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortPaymaster.deposit{value: 50 ether}(); } @@ -351,18 +323,18 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Cannot deposit 0 ether vm.expectRevert(); - openfortPaymaster.depositFor{value: 0 ether}(factoryAdmin); + openfortPaymaster.depositFor{value: 0 ether}(openfortAdmin); // Cannot depositFor using owner vm.prank(paymasterAdmin); vm.expectRevert(); openfortPaymaster.depositFor{value: 1 ether}(paymasterAdmin); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 0); // Paymaster deposits 1 ETH to EntryPoint - openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + openfortPaymaster.depositFor{value: 1 ether}(openfortAdmin); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 1 ether); // Get the WHOLE deposited amount so far assertEq(openfortPaymaster.getDeposit(), 52 ether); @@ -403,46 +375,46 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { vm.prank(paymasterAdmin); openfortPaymaster.withdrawDepositorTo(payable(paymasterAdmin), 1 ether); - // factoryAdmin can call it - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); - assertEq(factoryAdmin.balance, 100 ether); + // openfortAdmin can call it + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 1 ether); + assertEq(openfortAdmin.balance, 100 ether); // but not too much! vm.expectRevert(); - vm.prank(factoryAdmin); - openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1000 ether); + vm.prank(openfortAdmin); + openfortPaymaster.withdrawDepositorTo(payable(openfortAdmin), 1000 ether); // not using address 0! vm.expectRevert(); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortPaymaster.withdrawDepositorTo(payable(address(0)), 1 ether); - vm.prank(factoryAdmin); - openfortPaymaster.withdrawDepositorTo(payable(factoryAdmin), 1 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); - assertEq(factoryAdmin.balance, 101 ether); + vm.prank(openfortAdmin); + openfortPaymaster.withdrawDepositorTo(payable(openfortAdmin), 1 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 0 ether); + assertEq(openfortAdmin.balance, 101 ether); // Deposit of the owner is still 51 assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 51 ether); // Finally, let's test withdrawFromDepositor - // deposit again using factoryAdmin - openfortPaymaster.depositFor{value: 1 ether}(factoryAdmin); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + // deposit again using openfortAdmin + openfortPaymaster.depositFor{value: 1 ether}(openfortAdmin); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 1 ether); // Only owner cannot call it vm.expectRevert("Ownable: caller is not the owner"); - openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + openfortPaymaster.withdrawFromDepositor(openfortAdmin, payable(openfortAdmin), 1 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 1 ether); vm.expectRevert(); vm.prank(paymasterAdmin); - openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 100 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + openfortPaymaster.withdrawFromDepositor(openfortAdmin, payable(openfortAdmin), 100 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 1 ether); vm.expectRevert(OpenfortErrorsAndEvents.ZeroValueNotAllowed.selector); vm.prank(paymasterAdmin); - openfortPaymaster.withdrawFromDepositor(address(0), payable(factoryAdmin), 1 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 1 ether); + openfortPaymaster.withdrawFromDepositor(address(0), payable(openfortAdmin), 1 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 1 ether); vm.prank(paymasterAdmin); - openfortPaymaster.withdrawFromDepositor(factoryAdmin, payable(factoryAdmin), 1 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); + openfortPaymaster.withdrawFromDepositor(openfortAdmin, payable(openfortAdmin), 1 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 0 ether); } /* @@ -456,7 +428,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(testCounter), 0, @@ -482,7 +454,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); // MOCKSIG, "1", MOCKSIG to make sure we send 65 bytes as sig UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(testCounter), 0, @@ -509,7 +481,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { UserOperation[] memory userOps = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(testCounter), 0, @@ -536,14 +508,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = @@ -577,7 +549,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { UserOperation[] memory userOps = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(testCounter), 0, @@ -592,10 +564,10 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { { // Simulating that the factory admin gets the userOp and tries to sign it hash = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, hash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, hash); bytes memory signature = abi.encodePacked(r, s, v); bytes memory paymasterAndDataSigned = abi.encodePacked(address(openfortPaymaster), dataEncoded, signature); - assertEq(factoryAdmin, ECDSA.recover(hash, signature)); + assertEq(openfortAdmin, ECDSA.recover(hash, signature)); userOps[0].paymasterAndData = paymasterAndDataSigned; } @@ -603,14 +575,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(factoryAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(factoryAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = @@ -650,7 +622,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -680,14 +652,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -724,7 +696,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -753,14 +725,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -798,7 +770,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -827,14 +799,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -885,7 +857,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecuteBatch( - accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + accountAddress, openfortAdminPKey, bytes(""), targets, values, callData, paymasterAndData ); OpenfortPaymasterV2.PolicyStrategy memory strategy; @@ -909,14 +881,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -970,7 +942,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecuteBatch( - accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + accountAddress, openfortAdminPKey, bytes(""), targets, values, callData, paymasterAndData ); OpenfortPaymasterV2.PolicyStrategy memory strategy; @@ -994,14 +966,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -1055,7 +1027,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecuteBatch( - accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + accountAddress, openfortAdminPKey, bytes(""), targets, values, callData, paymasterAndData ); OpenfortPaymasterV2.PolicyStrategy memory strategy; @@ -1079,14 +1051,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -1132,7 +1104,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecuteBatch( - accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + accountAddress, openfortAdminPKey, bytes(""), targets, values, callData, paymasterAndData ); OpenfortPaymasterV2.PolicyStrategy memory strategy; @@ -1156,14 +1128,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -1216,7 +1188,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecuteBatch( - accountAddress, accountAdminPKey, bytes(""), targets, values, callData, paymasterAndData + accountAddress, openfortAdminPKey, bytes(""), targets, values, callData, paymasterAndData ); OpenfortPaymasterV2.PolicyStrategy memory strategy; @@ -1240,14 +1212,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -1291,7 +1263,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -1320,14 +1292,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -1364,20 +1336,20 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); - vm.prank(factoryAdmin); - openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); + vm.prank(openfortAdmin); + openfortPaymaster.depositFor{value: 50 ether}(openfortAdmin); assertEq(openfortPaymaster.getDeposit(), 100 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 50 ether); assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(factoryAdmin); + bytes memory dataEncoded = mockPaymasterDataERC20Dynamic(openfortAdmin); bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -1387,7 +1359,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { OpenfortPaymasterV2.PolicyStrategy memory strategy; strategy.paymasterMode = OpenfortPaymasterV2.Mode.DynamicRate; - strategy.depositor = factoryAdmin; + strategy.depositor = openfortAdmin; strategy.erc20Token = address(mockERC20); strategy.exchangeRate = EXCHANGERATE; @@ -1406,14 +1378,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -1434,7 +1406,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { assert(mockERC20.balanceOf(accountAddress) < TESTTOKEN_ACCOUNT_PREFUND); assert(openfortPaymaster.getDeposit() < 100 ether); - assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); + assert(openfortPaymaster.getDepositFor(openfortAdmin) < 50 ether); // deposit of the owner should have increased a bit because of the dust assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); } @@ -1448,22 +1420,22 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { mockERC20.mint(accountAddress, TESTTOKEN_ACCOUNT_PREFUND); assertEq(mockERC20.balanceOf(accountAddress), TESTTOKEN_ACCOUNT_PREFUND); - vm.prank(factoryAdmin); - openfortPaymaster.depositFor{value: 50 ether}(factoryAdmin); + vm.prank(openfortAdmin); + openfortPaymaster.depositFor{value: 50 ether}(openfortAdmin); assertEq(openfortPaymaster.getDeposit(), 100 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 50 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 50 ether); assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); uint256 pricePerTransaction = 10 ** 18; - bytes memory dataEncoded = mockPaymasterDataERC20Fixed(factoryAdmin, pricePerTransaction); + bytes memory dataEncoded = mockPaymasterDataERC20Fixed(openfortAdmin, pricePerTransaction); bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); // Create a userOp to let the paymaster use our mockERC20s UserOperation[] memory userOps = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(mockERC20), 0, @@ -1473,7 +1445,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { OpenfortPaymasterV2.PolicyStrategy memory strategy; strategy.paymasterMode = OpenfortPaymasterV2.Mode.FixedRate; - strategy.depositor = factoryAdmin; + strategy.depositor = openfortAdmin; strategy.erc20Token = address(mockERC20); strategy.exchangeRate = pricePerTransaction; @@ -1492,14 +1464,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = ECDSA.toEthSignedMessageHash(openfortPaymaster.getHash(userOps[0], VALIDUNTIL, VALIDAFTER, strategy)); @@ -1520,7 +1492,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { assert(mockERC20.balanceOf(accountAddress) < TESTTOKEN_ACCOUNT_PREFUND); assert(openfortPaymaster.getDeposit() < 100 ether); - assert(openfortPaymaster.getDepositFor(factoryAdmin) < 50 ether); + assert(openfortPaymaster.getDepositFor(openfortAdmin) < 50 ether); assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // deposit of the owner should have increased a bit because of the dust } @@ -1547,7 +1519,13 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { */ function test_requireFromEntryPoint() public { UserOperation[] memory userOpAux = _setupUserOpExecute( - accountAddress, accountAdminPKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()"), "" + accountAddress, + openfortAdminPKey, + bytes(""), + address(testCounter), + 0, + abi.encodeWithSignature("count()"), + "" ); vm.prank(paymasterAdmin); @@ -1566,17 +1544,17 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { assertEq(openfortPaymaster.owner(), paymasterAdmin); vm.expectRevert("Ownable: caller is not the owner"); - openfortPaymaster.transferOwnership(factoryAdmin); + openfortPaymaster.transferOwnership(openfortAdmin); vm.prank(paymasterAdmin); - openfortPaymaster.transferOwnership(factoryAdmin); + openfortPaymaster.transferOwnership(openfortAdmin); vm.expectRevert("Ownable2Step: caller is not the new owner"); openfortPaymaster.acceptOwnership(); - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortPaymaster.acceptOwnership(); - assertEq(openfortPaymaster.owner(), factoryAdmin); + assertEq(openfortPaymaster.owner(), openfortAdmin); } /* @@ -1588,50 +1566,50 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { // Play around with deposits assertEq(openfortPaymaster.getDeposit(), 50 ether); assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 0 ether); - openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); + openfortPaymaster.depositFor{value: 3 ether}(openfortAdmin); assertEq(openfortPaymaster.getDeposit(), 53 ether); assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 3 ether); vm.prank(paymasterAdmin); - openfortPaymaster.transferOwnership(factoryAdmin); + openfortPaymaster.transferOwnership(openfortAdmin); assertEq(openfortPaymaster.getDeposit(), 53 ether); assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 3 ether); // Play around with deposits - vm.prank(factoryAdmin); + vm.prank(openfortAdmin); openfortPaymaster.acceptOwnership(); - assertEq(openfortPaymaster.owner(), factoryAdmin); + assertEq(openfortPaymaster.owner(), openfortAdmin); // After transferring the ownership, the old owner does not have any deposit // and the new one has all deposit from previous owner PLUS its old deposit assertEq(openfortPaymaster.getDeposit(), 53 ether); assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 0 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 53 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 53 ether); } /* - * Test using a new depositor (factoryAdmin) + * Test using a new depositor (openfortAdmin) * Should work */ function testPaymasterUserOpNativeValidSigDEPOSITOR() public { - bytes memory dataEncoded = mockPaymasterDataNative(factoryAdmin); + bytes memory dataEncoded = mockPaymasterDataNative(openfortAdmin); assertEq(openfortPaymaster.getDeposit(), 50 ether); assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 0 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 0 ether); - openfortPaymaster.depositFor{value: 3 ether}(factoryAdmin); + openfortPaymaster.depositFor{value: 3 ether}(openfortAdmin); assertEq(openfortPaymaster.getDeposit(), 53 ether); assertEq(openfortPaymaster.getDepositFor(paymasterAdmin), 50 ether); - assertEq(openfortPaymaster.getDepositFor(factoryAdmin), 3 ether); + assertEq(openfortPaymaster.getDepositFor(openfortAdmin), 3 ether); bytes memory paymasterAndData = abi.encodePacked(address(openfortPaymaster), dataEncoded, MOCKSIG, "1", MOCKSIG); console.log("paymasterAndData"); @@ -1640,7 +1618,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { UserOperation[] memory userOps = _setupUserOpExecute( accountAddress, - accountAdminPKey, + openfortAdminPKey, bytes(""), address(testCounter), 0, @@ -1649,7 +1627,7 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { ); OpenfortPaymasterV2.PolicyStrategy memory strategy; strategy.paymasterMode = OpenfortPaymasterV2.Mode.PayForUser; - strategy.depositor = factoryAdmin; + strategy.depositor = openfortAdmin; strategy.erc20Token = address(0); strategy.exchangeRate = 0; bytes32 hash; @@ -1671,14 +1649,14 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { bytes memory userOpSignature; bytes32 hash2; { - bytes32 opHash = EntryPoint(entryPoint).getUserOpHash(userOps[0]); + bytes32 opHash = entryPoint.getUserOpHash(userOps[0]); bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(openfortAdminPKey, msgHash); userOpSignature = abi.encodePacked(r, s, v); // Verifications below commented to avoid "Stack too deep" error - assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(accountAdminPKey)); + assertEq(ECDSA.recover(msgHash, v, r, s), vm.addr(openfortAdminPKey)); // Should return account admin hash2 = @@ -1702,6 +1680,6 @@ contract OpenfortPaymasterV2Test is OpenfortBaseTest { assert(openfortPaymaster.getDeposit() < 53 ether); // less than 53 because the total cost have decreased assert(openfortPaymaster.getDepositFor(paymasterAdmin) > 50 ether); // more than 50 because the dust has gone to the owner deposit - assert(openfortPaymaster.getDepositFor(factoryAdmin) < 3 ether); // less than 3 because the gas cost was paid using factoryAdmin's deposit + assert(openfortPaymaster.getDepositFor(openfortAdmin) < 3 ether); // less than 3 because the gas cost was paid using openfortAdmin's deposit } } From b9e652d25a445e50c10e475a609bcb2fb7b28a9a Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 11:56:17 +0100 Subject: [PATCH 64/83] More cleanup --- test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol | 3 +-- .../core/upgradeable/UpgradeableOpenfortAccountTest.t.sol | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 328ff4f..60a269a 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -5,7 +5,6 @@ import {console} from "lib/forge-std/src/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; import {ManagedOpenfortAccount} from "contracts/core/managed/ManagedOpenfortAccount.sol"; import {ManagedOpenfortFactory} from "contracts/core/managed/ManagedOpenfortFactory.sol"; @@ -39,7 +38,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { ManagedOpenfortDeploy managedOpenfortDeploy = new ManagedOpenfortDeploy(); (managedOpenfortAccountImpl, openfortFactory) = managedOpenfortDeploy.run(); - // Create a managed account wallet and get its address + // Create a managed account and get its address vm.prank(openfortAdmin); accountAddress = openfortFactory.createAccountWithNonce(openfortAdmin, "1", true); } diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 94da641..79d1764 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -9,7 +9,6 @@ import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Recei import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {IEntryPoint, UserOperation} from "account-abstraction/core/EntryPoint.sol"; -import {TestCounter} from "account-abstraction/test/TestCounter.sol"; import {IBaseRecoverableAccount} from "contracts/interfaces/IBaseRecoverableAccount.sol"; import {IUpgradeableOpenfortAccount} from "contracts/interfaces/IUpgradeableOpenfortAccount.sol"; import {OpenfortErrorsAndEvents} from "contracts/interfaces/OpenfortErrorsAndEvents.sol"; @@ -40,7 +39,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { UpgradeableOpenfortDeploy upgradeableOpenfortDeploy = new UpgradeableOpenfortDeploy(); (upgradeableOpenfortAccountImpl, openfortFactory) = upgradeableOpenfortDeploy.run(); - // Create an upgradeable account wallet and get its address + // Create an upgradeable account and get its address vm.prank(openfortAdmin); accountAddress = openfortFactory.createAccountWithNonce(openfortAdmin, "1", true); } From d38d49c2fdafcd16073fbb4bc8a1f39e8a3af2c0 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 14:15:31 +0100 Subject: [PATCH 65/83] Fixing BRA-01 from CertiK audit --- .../core/base/BaseRecoverableAccount.sol | 1 + .../managed/ManagedOpenfortAccountTest.t.sol | 24 +++++++++++++++++++ .../UpgradeableOpenfortAccountTest.t.sol | 24 +++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 14824db..f2ad05d 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -218,6 +218,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg function proposeGuardian(address _guardian) external onlyOwner { if (isLocked()) revert AccountLocked(); if (owner() == _guardian) revert GuardianCannotBeOwner(); + if (pendingOwner() == _guardian) revert GuardianCannotBeOwner(); if (isGuardian(_guardian)) revert DuplicatedGuardian(); if (_guardian == address(0)) revert ZeroAddressNotAllowed(); diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 60a269a..482d8ed 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -2506,4 +2506,28 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertTrue(account.supportsInterface(type(IERC165).interfaceId)); assertFalse(account.supportsInterface(bytes4(0x0000))); } + + /* + * Testcase where a pending owner is proposed as guardian. It used to work, should fail now. + * From the CertiK audit, issue BRA-01 + */ + function testFailAddPendingOwnerAsGuardian() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); + + address newOwner = makeAddr("newOwner"); + vm.prank(openfortAdmin); + openfortAccount.transferOwnership(newOwner); + + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(newOwner); + + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(newOwner); + + vm.prank(newOwner); + openfortAccount.acceptOwnership(); + + assertEq(openfortAccount.isGuardian(newOwner), true); + assertEq(openfortAccount.owner(), newOwner); + } } diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 79d1764..def1881 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -2844,4 +2844,28 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.prank(openfortAdmin); openfortFactory.updateInitialGuardian(address(openfortAdmin)); } + + /* + * Testcase where a pending owner is proposed as guardian. It used to work, should fail now. + * From the CertiK audit, issue BRA-01 + */ + function testFailAddPendingOwnerAsGuardian() public { + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); + + address newOwner = makeAddr("newOwner"); + vm.prank(openfortAdmin); + openfortAccount.transferOwnership(newOwner); + + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(newOwner); + + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(newOwner); + + vm.prank(newOwner); + openfortAccount.acceptOwnership(); + + assertEq(openfortAccount.isGuardian(newOwner), true); + assertEq(openfortAccount.owner(), newOwner); + } } From 0a6084ef3627f9b7d01c6d1897300d5808f45179 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 15:28:57 +0100 Subject: [PATCH 66/83] Fixing BOA-01 from CertiK audit --- contracts/core/base/BaseOpenfortAccount.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 2b1f7f1..b3ac876 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -252,6 +252,7 @@ abstract contract BaseOpenfortAccount is _requireFromEntryPointOrOwner(); if (sessionKeys[_key].validUntil != 0) { sessionKeys[_key].validUntil = 0; + sessionKeys[_key].limit = 0; sessionKeys[_key].masterSessionKey = false; sessionKeys[_key].registrarAddress = address(0); emit SessionKeyRevoked(_key); From 9a5b19842f5477bf90773302129c7605ba654152 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 17:21:43 +0100 Subject: [PATCH 67/83] Fixed some tests --- .../managed/ManagedOpenfortAccountTest.t.sol | 17 +++++++++++++++-- .../UpgradeableOpenfortAccountTest.t.sol | 12 ++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 482d8ed..116875d 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -438,9 +438,22 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - vm.warp(100); + vm.warp(30); + vm.prank(openfortAdmin); + vm.expectRevert("Cannot register an expired session key"); + // Register a session key valid from 10 to 20 at time 30, should fail. + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 10, 20, 100, emptyWhitelist); + vm.prank(openfortAdmin); - ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); + // Register a session key valid from 10 to 50 at time 30, should work. + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 10, 50, 100, emptyWhitelist); + + // Cannot register same session key twice + vm.prank(openfortAdmin); + vm.expectRevert("SessionKey already registered"); + ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 20, 75, 100, emptyWhitelist); + + vm.warp(200); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index def1881..aae0155 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -534,9 +534,17 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); address[] memory emptyWhitelist; - vm.warp(100); + vm.warp(30); + vm.prank(openfortAdmin); + vm.expectRevert("Cannot register an expired session key"); + // Register a session key valid from 10 to 20 at time 30, should fail. + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 10, 20, 100, emptyWhitelist); + vm.prank(openfortAdmin); - IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 0, 99, 100, emptyWhitelist); + // Register a session key valid from 10 to 50 at time 30, should work. + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 10, 50, 100, emptyWhitelist); + + vm.warp(200); UserOperation[] memory userOp = _setupUserOpExecute( accountAddress, sessionKeyPrivKey, bytes(""), address(testCounter), 0, abi.encodeWithSignature("count()") From b497003100592df196cf785799bfcc6cf09df183 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 17:22:26 +0100 Subject: [PATCH 68/83] fmt --- test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol | 2 +- .../core/upgradeable/UpgradeableOpenfortAccountTest.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 116875d..526c0e8 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -443,7 +443,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Cannot register an expired session key"); // Register a session key valid from 10 to 20 at time 30, should fail. ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 10, 20, 100, emptyWhitelist); - + vm.prank(openfortAdmin); // Register a session key valid from 10 to 50 at time 30, should work. ManagedOpenfortAccount(payable(accountAddress)).registerSessionKey(sessionKey, 10, 50, 100, emptyWhitelist); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index aae0155..c469d2d 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -539,7 +539,7 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { vm.expectRevert("Cannot register an expired session key"); // Register a session key valid from 10 to 20 at time 30, should fail. IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 10, 20, 100, emptyWhitelist); - + vm.prank(openfortAdmin); // Register a session key valid from 10 to 50 at time 30, should work. IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey(sessionKey, 10, 50, 100, emptyWhitelist); From dfe0dea9bcce89d61c972decb2b4d25fff4e8fc0 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 17:22:32 +0100 Subject: [PATCH 69/83] Fixing BOA-02 from CertiK audit --- contracts/core/base/BaseOpenfortAccount.sol | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index b3ac876..30c53aa 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -105,12 +105,13 @@ abstract contract BaseOpenfortAccount is /* * @notice Return whether a sessionKey is valid. */ - function isValidSessionKey(address _sessionKey, bytes calldata _callData) public virtual returns (bool) { + function isValidSessionKey(address _sessionKey, bytes calldata _callData) internal virtual returns (bool) { SessionKeyStruct storage sessionKey = sessionKeys[_sessionKey]; // If not owner and the session key is revoked, return false if (sessionKey.validUntil == 0) return false; // If the sessionKey was not registered by the owner, return false + // If the account is transferred or sold, isValidSessionKey() will return false with old session keys if (sessionKey.registrarAddress != owner()) return false; // If the signer is a session key that is still valid @@ -218,7 +219,9 @@ abstract contract BaseOpenfortAccount is address[] calldata _whitelist ) external virtual { _requireFromEntryPointOrOwner(); - + require(_validUntil > block.timestamp, "Cannot register an expired session key"); + require(_validAfter < _validUntil, "_validAfter must be lower than _validUntil"); + require(sessionKeys[_key].validUntil == 0, "SessionKey already registered"); require(_whitelist.length < 11, "Whitelist too big"); uint256 i; for (i; i < _whitelist.length;) { @@ -228,10 +231,11 @@ abstract contract BaseOpenfortAccount is } } if (i != 0) { - // If there was some whitelisting, it is not a masterSessionKey + // If there is some whitelisting, it is not a masterSessionKey sessionKeys[_key].whitelisting = true; sessionKeys[_key].masterSessionKey = false; } else { + // If there is some limit, it is not a masterSessionKey if (_limit == ((2 ** 48) - 1)) sessionKeys[_key].masterSessionKey = true; else sessionKeys[_key].masterSessionKey = false; } From 1144be4ee4c3ef5a172b178b1b8c673a3db90b0c Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 18:06:13 +0100 Subject: [PATCH 70/83] Fixing BOA-03 from CertiK audit --- contracts/core/base/BaseOpenfortAccount.sol | 2 +- .../UpgradeableOpenfortAccountTest.t.sol | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 30c53aa..1eb0c07 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -91,7 +91,7 @@ abstract contract BaseOpenfortAccount is // If the signer is a session key that is still valid if ( sessionKey.validUntil == 0 || sessionKey.validAfter > block.timestamp - || sessionKey.validUntil < block.timestamp || sessionKey.limit < 1 + || sessionKey.validUntil < block.timestamp || (!sessionKey.masterSessionKey && sessionKey.limit < 1) ) { return 0xffffffff; } // Not owner or session key revoked diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index c469d2d..8f598e0 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -1258,6 +1258,74 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(valid, MAGICVALUE); // SHOULD PASS } + function testisValidSignatureTypedSessionKeyNotRegistered() public { + address sessionKey; + uint256 sessionKeyPrivKey; + (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); + + // Original owner registers a session key + vm.prank(openfortAdmin); + openfortAccount.registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); + + string memory messageToSign = "Signed by Owner"; + bytes32 hash = keccak256(abi.encodePacked(messageToSign)); + + bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, hash)); + + (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = + IERC5267(accountAddress).eip712Domain(); + + bytes32 domainSeparator = keccak256( + abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) + ); + + bytes memory signature = getEIP712SignatureFrom(accountAddress, structHash, sessionKeyPrivKey); + bytes32 hash712 = domainSeparator.toTypedDataHash(structHash); + address signer = hash712.recover(signature); + + assertEq(sessionKey, signer); // [PASS] + + bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); + assertNotEq(valid, bytes4(0xffffffff)); // SHOULD PASS + assertEq(valid, MAGICVALUE); // SHOULD PASS + } + + function testisValidSignatureTypedSessionKey() public { + address sessionKey; + uint256 sessionKeyPrivKey; + (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + + // address[] memory emptyWhitelist; + // IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); + // Original owner registers a session key + // vm.prank(openfortAdmin); + // openfortAccount.registerSessionKey(sessionKey, 0, 2 ** 48 - 1, 100, emptyWhitelist); + + string memory messageToSign = "Signed by Owner"; + bytes32 hash = keccak256(abi.encodePacked(messageToSign)); + + bytes32 structHash = keccak256(abi.encode(OF_MSG_TYPEHASH, hash)); + + (, string memory name, string memory version, uint256 chainId, address verifyingContract,,) = + IERC5267(accountAddress).eip712Domain(); + + bytes32 domainSeparator = keccak256( + abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract) + ); + + bytes memory signature = getEIP712SignatureFrom(accountAddress, structHash, sessionKeyPrivKey); + bytes32 hash712 = domainSeparator.toTypedDataHash(structHash); + address signer = hash712.recover(signature); + + assertEq(sessionKey, signer); // [PASS] + + bytes4 valid = IBaseRecoverableAccount(payable(accountAddress)).isValidSignature(hash, signature); + assertEq(valid, bytes4(0xffffffff)); // SHOULD PASS + assertNotEq(valid, MAGICVALUE); // SHOULD PASS + } + function testAddDeposit() public { IBaseRecoverableAccount account = IBaseRecoverableAccount(payable(accountAddress)); assertEq(0, account.getDeposit()); From 9f28d4292abcbd41b752e1cd407dc6b7f19d9e23 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 18:20:36 +0100 Subject: [PATCH 71/83] Fixing BRA-02 from CertiK audit --- .../core/base/BaseRecoverableAccount.sol | 2 + .../UpgradeableOpenfortAccountTest.t.sol | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index f2ad05d..61c6c75 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -257,6 +257,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg */ function cancelGuardianProposal(address _guardian) external onlyOwner { if (isLocked()) revert AccountLocked(); + if (isGuardian(_guardian)) revert UnknownProposal(); if (guardiansConfig.info[_guardian].pending == 0) revert UnknownProposal(); guardiansConfig.info[_guardian].pending = 0; emit GuardianProposalCancelled(_guardian); @@ -308,6 +309,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg */ function cancelGuardianRevocation(address _guardian) external onlyOwner { if (isLocked()) revert AccountLocked(); + if (!isGuardian(_guardian)) revert UnknownRevoke(); if (guardiansConfig.info[_guardian].pending == 0) revert UnknownRevoke(); guardiansConfig.info[_guardian].pending = 0; emit GuardianRevocationCancelled(_guardian); diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 8f598e0..3b969f1 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -1698,6 +1698,48 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(openfortAccount.isGuardian(friendAccount), false); } + /* + * Test proposing a guardian and cancel its REVOKATION (which should not make sense). + * Related to BRA-02 isee form CertiK audit. + */ + function testFailBRA02Issue() public { + IBaseRecoverableAccount openfortAccount = IBaseRecoverableAccount(payable(accountAddress)); + + // Verify that the number of guardians is 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + + // Create a friend EOA + address friendAccount = makeAddr("friend"); + + // Trying to propose a guardian not using the owner + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.proposeGuardian(friendAccount); + + // Expect that we will see an event containing the friend account and security period + vm.expectEmit(true, true, false, true); + emit GuardianProposed(friendAccount, block.timestamp + SECURITY_PERIOD); + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(friendAccount); + + // Verify that the number of guardians is still 1 (default) + assertEq(openfortAccount.guardianCount(), 1); + // Friend account should not be a guardian yet + assertEq(openfortAccount.isGuardian(friendAccount), false); + + skip(1); + vm.expectRevert(PendingProposalNotOver.selector); + openfortAccount.confirmGuardianProposal(friendAccount); + + skip(SECURITY_PERIOD); + vm.expectRevert("Ownable: caller is not the owner"); + openfortAccount.cancelGuardianRevocation(friendAccount); + + vm.expectEmit(true, true, false, true); + emit GuardianRevocationCancelled(friendAccount); + vm.prank(openfortAdmin); + openfortAccount.cancelGuardianRevocation(friendAccount); + } + /* * Test proposing owner as guardian. It should revert. * Successfully propose a guardian and confirm it after SECURITY_PERIOD From d18c692fd51af7f9f8058f8f8db8ce19dce13b32 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 18:24:22 +0100 Subject: [PATCH 72/83] Fixing BRA-03 from CertiK audit --- contracts/core/base/BaseRecoverableAccount.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 61c6c75..878fc5d 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -270,6 +270,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg */ function revokeGuardian(address _guardian) external onlyOwner { if (!isGuardian(_guardian)) revert MustBeGuardian(); + if (isLocked()) revert AccountLocked(); if ( guardiansConfig.info[_guardian].pending > 0 && block.timestamp < guardiansConfig.info[_guardian].pending + securityWindow @@ -286,6 +287,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg */ function confirmGuardianRevocation(address _guardian) external { if (guardiansConfig.info[_guardian].pending == 0) revert UnknownRevoke(); + if (isLocked()) revert AccountLocked(); if (!isGuardian(_guardian)) revert MustBeGuardian(); if (guardiansConfig.info[_guardian].pending > block.timestamp) revert PendingRevokeNotOver(); if (block.timestamp > guardiansConfig.info[_guardian].pending + securityWindow) revert PendingRevokeExpired(); From de75b35cb7df2fc5d1ee37858f8d204854a6e62d Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Mon, 18 Dec 2023 19:01:08 +0100 Subject: [PATCH 73/83] Fixing BRA-05 from CertiK audit --- contracts/core/base/BaseRecoverableAccount.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 878fc5d..5246987 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -224,7 +224,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg if ( guardiansConfig.info[_guardian].pending != 0 - && block.timestamp < guardiansConfig.info[_guardian].pending + securityWindow + && block.timestamp <= guardiansConfig.info[_guardian].pending + securityWindow ) { revert DuplicatedProposal(); } From 5c9ee0ef40f27946ef3e518e9478daa0ec74f368 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Tue, 19 Dec 2023 17:38:45 +0100 Subject: [PATCH 74/83] Improved ERC1155 test due to ERO-01 (CertiK) --- .../erc6551/ERC6551OpenfortAccountTest.sol | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol index 0f5432d..3b53a34 100644 --- a/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol +++ b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol @@ -329,6 +329,12 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { assertTrue(erc6551OpenfortAccount.supportsInterface(type(IERC1155Receiver).interfaceId)); assertTrue(erc6551OpenfortAccount.supportsInterface(type(IERC165).interfaceId)); assertFalse(erc6551OpenfortAccount.supportsInterface(bytes4(0x0000))); + assertEq(erc6551OpenfortAccount.onERC1155Received(address(0), address(0), 0, 0, ""), bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))); + uint256[] memory ids = new uint256[](1); + ids[0] = 1; + uint256[] memory values = new uint256[](1); + values[0] = 1; + assertEq(erc6551OpenfortAccount.onERC1155BatchReceived(address(0), address(0), ids, values, ""), bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))); } function testUpdateEntryPoint() public { @@ -376,4 +382,37 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { vm.prank(openfortAdmin); mockERC721.safeTransferFrom(openfortAdmin, accountAddress2, 1); } + + /* + * Test onERC721Received() + */ + function testSafeTransferFrom1155() public { + mockERC1155.mint(openfortAdmin, 7, 7); + + assertEq(mockERC1155.balanceOf(openfortAdmin, 7), 7); + assertEq(mockERC1155.balanceOf(address(erc6551OpenfortAccount), 7), 0); + + vm.prank(openfortAdmin); + mockERC1155.safeTransferFrom(openfortAdmin, address(erc6551OpenfortAccount), 7, 7, ""); + + assertEq(mockERC1155.balanceOf(openfortAdmin, 7), 0); + assertEq(mockERC1155.balanceOf(address(erc6551OpenfortAccount), 7), 7); + + vm.prank(openfortAdmin); + erc6551OpenfortAccount.execute{value: 1 ether}( + address(mockERC1155), + 0, + abi.encodeWithSignature( + "safeTransferFrom(address,address,uint256,uint256,bytes)", + address(erc6551OpenfortAccount), + openfortAdmin, + 7, + 7, + "" + ) + ); + + assertEq(mockERC1155.balanceOf(openfortAdmin, 7), 7); + assertEq(mockERC1155.balanceOf(address(erc6551OpenfortAccount), 7), 0); + } } From 073798daf754f4fa0a5a662eb1f3c38c656a4ddb Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 20 Dec 2023 11:38:47 +0100 Subject: [PATCH 75/83] Fixing BOA-04 from CertiK audit --- contracts/core/base/BaseOpenfortAccount.sol | 43 +++++++++------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 1eb0c07..019ff17 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -120,6 +120,14 @@ abstract contract BaseOpenfortAccount is _callData[0] | (bytes4(_callData[1]) >> 8) | (bytes4(_callData[2]) >> 16) | (bytes4(_callData[3]) >> 24); if (funcSelector == EXECUTE_SELECTOR) { + address toContract; + (toContract,,) = abi.decode(_callData[4:], (address, uint256, bytes)); + // Check if reenter, do not allow + if (toContract == address(this)) return false; + + // Check if it is a masterSessionKey + if (sessionKey.masterSessionKey) return true; + // Limit of transactions per sessionKey reached if (sessionKey.limit == 0) return false; // Deduct one use of the limit for the given session key @@ -127,42 +135,29 @@ abstract contract BaseOpenfortAccount is sessionKey.limit = sessionKey.limit - 1; } - // Check if it is a masterSessionKey - if (sessionKey.masterSessionKey) { - return true; - } - - // If it is not a masterSessionKey, let's check for whitelisting and reentrancy - address toContract; - (toContract,,) = abi.decode(_callData[4:], (address, uint256, bytes)); - if (toContract == address(this)) { - return false; - } // Only masterSessionKey can reenter - // If there is no whitelist or there is, but the target is whitelisted, return true - if (!sessionKey.whitelisting || sessionKey.whitelist[toContract]) { - return true; - } + if (!sessionKey.whitelisting || sessionKey.whitelist[toContract]) return true; return false; // All other cases, deny } else if (funcSelector == EXECUTEBATCH_SELECTOR) { (address[] memory toContracts,,) = abi.decode(_callData[4:], (address[], uint256[], bytes[])); // Check if limit of transactions per sessionKey reached if (sessionKey.limit < toContracts.length || toContracts.length > 9) return false; - unchecked { - sessionKey.limit = sessionKey.limit - SafeCastUpgradeable.toUint48(toContracts.length); + if (!sessionKey.masterSessionKey) { + unchecked { + sessionKey.limit = sessionKey.limit - SafeCastUpgradeable.toUint48(toContracts.length); + } } - // Check if it is a masterSessionKey (no whitelist applies) - if (sessionKey.masterSessionKey) return true; uint256 i; for (i; i < toContracts.length;) { - if (toContracts[i] == address(this)) { - return false; - } // Only masterSessionKey can reenter - if (sessionKey.whitelisting && !sessionKey.whitelist[toContracts[i]]) { + // Check if reenter, do not allow + if (toContracts[i] == address(this)) return false; + + // If not masterSessionKey, check whitelist + if (!sessionKey.masterSessionKey && sessionKey.whitelisting && !sessionKey.whitelist[toContracts[i]]) { return false; - } // One contract's not in the sessionKey's whitelist (if any) + } unchecked { ++i; } From d72d7a6385d3f4d0058cbec7acfb4bb96da2e4f8 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Wed, 20 Dec 2023 11:38:58 +0100 Subject: [PATCH 76/83] Fmt --- .../core/erc6551/ERC6551OpenfortAccountTest.sol | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol index 3b53a34..4fb3205 100644 --- a/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol +++ b/test/foundry/core/erc6551/ERC6551OpenfortAccountTest.sol @@ -329,12 +329,18 @@ contract ERC6551OpenfortAccountTest is OpenfortBaseTest { assertTrue(erc6551OpenfortAccount.supportsInterface(type(IERC1155Receiver).interfaceId)); assertTrue(erc6551OpenfortAccount.supportsInterface(type(IERC165).interfaceId)); assertFalse(erc6551OpenfortAccount.supportsInterface(bytes4(0x0000))); - assertEq(erc6551OpenfortAccount.onERC1155Received(address(0), address(0), 0, 0, ""), bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))); + assertEq( + erc6551OpenfortAccount.onERC1155Received(address(0), address(0), 0, 0, ""), + bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) + ); uint256[] memory ids = new uint256[](1); ids[0] = 1; uint256[] memory values = new uint256[](1); values[0] = 1; - assertEq(erc6551OpenfortAccount.onERC1155BatchReceived(address(0), address(0), ids, values, ""), bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))); + assertEq( + erc6551OpenfortAccount.onERC1155BatchReceived(address(0), address(0), ids, values, ""), + bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)")) + ); } function testUpdateEntryPoint() public { From 42724b8c50c64fdf9217077d538d321646650478 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 22 Dec 2023 09:39:30 +0100 Subject: [PATCH 77/83] Further fixing BRA-01 from CertiK audit --- .../core/base/BaseRecoverableAccount.sol | 6 +++++ .../managed/ManagedOpenfortAccountTest.t.sol | 25 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 5246987..86829e6 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -420,6 +420,12 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg function transferOwnership(address _newOwner) public override { if (isLocked()) revert AccountLocked(); if (isGuardian(_newOwner)) revert GuardianCannotBeOwner(); + if ( + guardiansConfig.info[_newOwner].pending != 0 + && block.timestamp <= guardiansConfig.info[_newOwner].pending + securityWindow + ) { + revert GuardianCannotBeOwner(); + } super.transferOwnership(_newOwner); } diff --git a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol index 526c0e8..9c038f3 100644 --- a/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol +++ b/test/foundry/core/managed/ManagedOpenfortAccountTest.t.sol @@ -2524,7 +2524,7 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { * Testcase where a pending owner is proposed as guardian. It used to work, should fail now. * From the CertiK audit, issue BRA-01 */ - function testFailAddPendingOwnerAsGuardian() public { + function testFailAddPendingOwnerAsGuardian1() public { ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); address newOwner = makeAddr("newOwner"); @@ -2543,4 +2543,27 @@ contract ManagedOpenfortAccountTest is OpenfortBaseTest { assertEq(openfortAccount.isGuardian(newOwner), true); assertEq(openfortAccount.owner(), newOwner); } + + /* + * Testcase where a pending owner is proposed as guardian. It used to work, should fail now. + * From the CertiK audit, issue BRA-01 + */ + function testFailAddPendingOwnerAsGuardian2() public { + ManagedOpenfortAccount openfortAccount = ManagedOpenfortAccount(payable(accountAddress)); + address newOwner = makeAddr("newOwner"); + + vm.prank(openfortAdmin); + openfortAccount.proposeGuardian(newOwner); + + vm.prank(openfortAdmin); + openfortAccount.transferOwnership(newOwner); + + skip(SECURITY_PERIOD); + openfortAccount.confirmGuardianProposal(newOwner); + + vm.prank(newOwner); + openfortAccount.acceptOwnership(); + + assertEq(openfortAccount.isGuardian(newOwner), true); + } } From f0023272217a802b9fe3f4bc6460173732f79a0d Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 22 Dec 2023 09:40:04 +0100 Subject: [PATCH 78/83] Further fixing BOA-04 from CertiK audit --- contracts/core/base/BaseOpenfortAccount.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 019ff17..7104186 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -141,9 +141,9 @@ abstract contract BaseOpenfortAccount is return false; // All other cases, deny } else if (funcSelector == EXECUTEBATCH_SELECTOR) { (address[] memory toContracts,,) = abi.decode(_callData[4:], (address[], uint256[], bytes[])); - // Check if limit of transactions per sessionKey reached - if (sessionKey.limit < toContracts.length || toContracts.length > 9) return false; if (!sessionKey.masterSessionKey) { + // Check if limit of transactions per sessionKey reached + if (sessionKey.limit < toContracts.length || toContracts.length > 9) return false; unchecked { sessionKey.limit = sessionKey.limit - SafeCastUpgradeable.toUint48(toContracts.length); } From 622c2b5a9d508f7da55c9bc51f01193b44fa1604 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 22 Dec 2023 09:44:51 +0100 Subject: [PATCH 79/83] Further fixing BRA-05 from CertiK audit --- contracts/core/base/BaseRecoverableAccount.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 86829e6..6c668b7 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -273,7 +273,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg if (isLocked()) revert AccountLocked(); if ( guardiansConfig.info[_guardian].pending > 0 - && block.timestamp < guardiansConfig.info[_guardian].pending + securityWindow + && block.timestamp <= guardiansConfig.info[_guardian].pending + securityWindow ) revert DuplicatedRevoke(); // TODO need to allow if confirmation window passed guardiansConfig.info[_guardian].pending = block.timestamp + securityPeriod; From 6d19c2afc5f036f19dd6024c3c03154df53e4224 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 22 Dec 2023 10:25:56 +0100 Subject: [PATCH 80/83] Partially fixing COR-04 from CertiK audit --- contracts/core/base/BaseRecoverableAccount.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/core/base/BaseRecoverableAccount.sol b/contracts/core/base/BaseRecoverableAccount.sol index 6c668b7..4eb2ed6 100644 --- a/contracts/core/base/BaseRecoverableAccount.sol +++ b/contracts/core/base/BaseRecoverableAccount.sol @@ -104,6 +104,7 @@ abstract contract BaseRecoverableAccount is BaseOpenfortAccount, Ownable2StepUpg revert InsecurePeriod(); } emit EntryPointUpdated(entrypointContract, _entrypoint); + __Ownable2Step_init(); _transferOwnership(_defaultAdmin); entrypointContract = _entrypoint; __EIP712_init("Openfort", "0.5"); From 89ad211c876a2302627332e3b3cacd98e1e899a1 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 22 Dec 2023 10:30:29 +0100 Subject: [PATCH 81/83] running fmt --- script/OpenfortForksConfig.s.sol | 1 + test/foundry/core/OpenfortBaseTest.t.sol | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/script/OpenfortForksConfig.s.sol b/script/OpenfortForksConfig.s.sol index c513f58..5dfd988 100644 --- a/script/OpenfortForksConfig.s.sol +++ b/script/OpenfortForksConfig.s.sol @@ -33,6 +33,7 @@ abstract contract OpenfortForksConfig is Script { ArbitrumNovaFork, BaseFork, BeamFork // NUM_ACCEPTED_CHAINS-1 + } address[NUM_ACCEPTED_CHAINS] internal paymasterAddresses; diff --git a/test/foundry/core/OpenfortBaseTest.t.sol b/test/foundry/core/OpenfortBaseTest.t.sol index d3ae430..1c162f2 100644 --- a/test/foundry/core/OpenfortBaseTest.t.sol +++ b/test/foundry/core/OpenfortBaseTest.t.sol @@ -177,7 +177,6 @@ contract OpenfortBaseTest is Test, CheckOrDeployEntryPoint { /** * AA events */ - event Deposited(address indexed account, uint256 totalDeposit); /* From 9597226fb26123fc6f69c20c0ae2a9e5662914bc Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 22 Dec 2023 11:17:00 +0100 Subject: [PATCH 82/83] Further fixing BOA-04 from CertiK audit --- contracts/core/base/BaseOpenfortAccount.sol | 10 ++- .../UpgradeableOpenfortAccountTest.t.sol | 82 +++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/contracts/core/base/BaseOpenfortAccount.sol b/contracts/core/base/BaseOpenfortAccount.sol index 7104186..b907c77 100644 --- a/contracts/core/base/BaseOpenfortAccount.sol +++ b/contracts/core/base/BaseOpenfortAccount.sol @@ -141,16 +141,18 @@ abstract contract BaseOpenfortAccount is return false; // All other cases, deny } else if (funcSelector == EXECUTEBATCH_SELECTOR) { (address[] memory toContracts,,) = abi.decode(_callData[4:], (address[], uint256[], bytes[])); + uint256 numberOfInteractions = toContracts.length; + if (numberOfInteractions > 9) return false; if (!sessionKey.masterSessionKey) { - // Check if limit of transactions per sessionKey reached - if (sessionKey.limit < toContracts.length || toContracts.length > 9) return false; + // Check if limit of transactions per sessionKey is reached + if (sessionKey.limit < numberOfInteractions) return false; unchecked { - sessionKey.limit = sessionKey.limit - SafeCastUpgradeable.toUint48(toContracts.length); + sessionKey.limit = sessionKey.limit - SafeCastUpgradeable.toUint48(numberOfInteractions); } } uint256 i; - for (i; i < toContracts.length;) { + for (i; i < numberOfInteractions;) { // Check if reenter, do not allow if (toContracts[i] == address(this)) return false; diff --git a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol index 3b969f1..be91bef 100644 --- a/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol +++ b/test/foundry/core/upgradeable/UpgradeableOpenfortAccountTest.t.sol @@ -281,6 +281,88 @@ contract UpgradeableOpenfortAccountTest is OpenfortBaseTest { assertEq(testCounter.counters(accountAddress), count); } + /* + * Use the executeBatch() with a big number + */ + function testBatchingBigMasterSessionKey() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + address sessionKey; + uint256 sessionKeyPrivKey; + (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; + + // Register masterSessionKey + vm.prank(openfortAdmin); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey( + sessionKey, 0, 2 ** 48 - 1, 2 ** 48 - 1, emptyWhitelist + ); + + uint256 count = 9; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + for (uint256 i = 0; i < count; i += 1) { + targets[i] = address(testCounter); + values[i] = 0; + callData[i] = abi.encodeWithSignature("count()"); + } + + UserOperation[] memory userOp = + _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + // Verify that the counter has increased + assertEq(testCounter.counters(accountAddress), count); + } + + /* + * Use the executeBatch() with a "too big" number + */ + function testFailBatchingBigMasterSessionKey() public { + // Verify that the counter is still set to 0 + assertEq(testCounter.counters(accountAddress), 0); + + address sessionKey; + uint256 sessionKeyPrivKey; + (sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey"); + address[] memory emptyWhitelist; + + // Register masterSessionKey + vm.prank(openfortAdmin); + IBaseRecoverableAccount(payable(accountAddress)).registerSessionKey( + sessionKey, 0, 2 ** 48 - 1, 2 ** 48 - 1, emptyWhitelist + ); + + uint256 count = 10; + address[] memory targets = new address[](count); + uint256[] memory values = new uint256[](count); + bytes[] memory callData = new bytes[](count); + + for (uint256 i = 0; i < count; i += 1) { + targets[i] = address(testCounter); + values[i] = 0; + callData[i] = abi.encodeWithSignature("count()"); + } + + UserOperation[] memory userOp = + _setupUserOpExecuteBatch(accountAddress, sessionKeyPrivKey, bytes(""), targets, values, callData); + + entryPoint.depositTo{value: 1 ether}(accountAddress); + vm.expectRevert(); + entryPoint.simulateValidation(userOp[0]); + entryPoint.handleOps(userOp, beneficiary); + + // Verify that the counter has increased + assertEq(testCounter.counters(accountAddress), count); + } + /* * Should fail, try to use a sessionKey that is not registered. */ From b623cccf3e19c987eb889c32f58b9df9abbcacf9 Mon Sep 17 00:00:00 2001 From: Eloi Manuel Date: Fri, 22 Dec 2023 12:21:15 +0100 Subject: [PATCH 83/83] Adding upgrade scripts --- script/upgradeManagedAccounts.s.sol | 50 ++++++++++++++++++++++++++ script/upgradeUpgradeableAccount.s.sol | 37 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 script/upgradeManagedAccounts.s.sol create mode 100644 script/upgradeUpgradeableAccount.s.sol diff --git a/script/upgradeManagedAccounts.s.sol b/script/upgradeManagedAccounts.s.sol new file mode 100644 index 0000000..22ec6ea --- /dev/null +++ b/script/upgradeManagedAccounts.s.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {Script, console} from "forge-std/Script.sol"; +import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; +import {ManagedOpenfortAccount} from "../contracts/core/managed/ManagedOpenfortAccount.sol"; +import {ManagedOpenfortFactory} from "../contracts/core/managed/ManagedOpenfortFactory.sol"; +import {CheckOrDeployEntryPoint} from "script/aux/checkOrDeployEntryPoint.sol"; + +contract ManagedOpenfortUpgrade is Script, CheckOrDeployEntryPoint { + uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); + address internal deployAddress = vm.addr(deployPrivKey); + + address internal factoryAddress = 0x44A7d7B291834442EE3703bD8bB7f91eD6F2577E; + address internal oldImplementation = 0x36604309934A2Fc92C3445Cf4566b23b5b4BbAad; + + function run() + public + returns (ManagedOpenfortAccount managedOpenfortAccountImpl, ManagedOpenfortFactory openfortFactory) + { + bytes32 versionSalt = vm.envBytes32("VERSION_SALT"); + openfortFactory = ManagedOpenfortFactory(factoryAddress); + + assert(openfortFactory.implementation() == oldImplementation); + address accountImpl = openfortFactory.implementation(); + console.log("Old account implementation: ", accountImpl); + + address exampleAccountAddress = openfortFactory.createAccountWithNonce(deployAddress, "1", true); + console.log("Example account address: ", exampleAccountAddress); + + vm.startBroadcast(deployPrivKey); + // Create an acccount to serve as new implementation + managedOpenfortAccountImpl = new ManagedOpenfortAccount{salt: versionSalt}(); + + // Update the account implementation + openfortFactory.upgradeTo(address(managedOpenfortAccountImpl)); + assert(openfortFactory.implementation() == address(managedOpenfortAccountImpl)); + + vm.stopBroadcast(); + + accountImpl = openfortFactory.implementation(); + console.log("New account implementation: ", accountImpl); + + // Create a managed account and get its address + address exampleAccountAddress2 = openfortFactory.createAccountWithNonce(deployAddress, "1", true); + console.log("Example account address 2: ", exampleAccountAddress2); + + assert(exampleAccountAddress == exampleAccountAddress2); + } +} diff --git a/script/upgradeUpgradeableAccount.s.sol b/script/upgradeUpgradeableAccount.s.sol new file mode 100644 index 0000000..8083652 --- /dev/null +++ b/script/upgradeUpgradeableAccount.s.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity =0.8.19; + +import {Script, console} from "forge-std/Script.sol"; +import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol"; +import {UpgradeableOpenfortAccount} from "../contracts/core/upgradeable/UpgradeableOpenfortAccount.sol"; +import {UpgradeableOpenfortProxy} from "../contracts/core/upgradeable/UpgradeableOpenfortProxy.sol"; +import {CheckOrDeployEntryPoint} from "script/aux/checkOrDeployEntryPoint.sol"; + +contract UpgradeableOpenfortDeploy is Script, CheckOrDeployEntryPoint { + uint256 internal deployPrivKey = vm.envUint("PK_PAYMASTER_OWNER_TESTNET"); + address internal deployAddress = vm.addr(deployPrivKey); + IEntryPoint internal entryPoint; + + address internal accountAddress = address(0); + address internal newImplementation = address(0); + + event AccountImplementationDeployed(address indexed creator); + + function run() public { + UpgradeableOpenfortProxy proxy = UpgradeableOpenfortProxy(payable(accountAddress)); + UpgradeableOpenfortAccount account = UpgradeableOpenfortAccount(payable(accountAddress)); + + address accountImpl = proxy.implementation(); + console.log("Old account implementation: ", accountImpl); + + vm.startBroadcast(deployPrivKey); + + // Update the account implementation + account.upgradeTo(newImplementation); + + vm.stopBroadcast(); + + accountImpl = proxy.implementation(); + console.log("New account implementation: ", accountImpl); + } +}