From cc85a74efdb63cffd16d25d466bb7b0e9d265073 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 28 Nov 2023 23:34:30 -0500 Subject: [PATCH] perf: precompile contracts for faster test runs --- .github/workflows/test.yml | 20 +++---- .gitignore | 5 ++ README.md | 18 +++--- foundry.toml | 32 +++++++--- test/TestUtils.sol | 21 ------- test/account/AccountLoupe.t.sol | 7 +-- test/account/AccountReturnData.t.sol | 7 +-- .../ExecuteFromPluginPermissions.t.sol | 9 ++- test/account/ManifestValidity.t.sol | 7 +-- test/account/UpgradeableModularAccount.t.sol | 9 +-- test/account/ValidationIntersection.t.sol | 7 +-- test/mocks/MSCAFactoryFixture.sol | 6 +- test/plugin/SingleOwnerPlugin.t.sol | 7 +-- test/plugin/TokenReceiverPlugin.t.sol | 10 ++-- test/utils/OptimizedTest.sol | 58 +++++++++++++++++++ 15 files changed, 136 insertions(+), 87 deletions(-) delete mode 100644 test/TestUtils.sol create mode 100644 test/utils/OptimizedTest.sol diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a8527e96..dfa5cf41 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: ERC6900 RI Test CI +name: ERC-6900 RI Test CI on: [pull_request, workflow_dispatch] @@ -45,9 +45,9 @@ jobs: - name: "Lint the contracts" run: "pnpm lint" - - test: - name: Run Forge Tests + + test-optimized-test-deep: + name: Run Forge Tests (optimized-test-deep) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -63,13 +63,13 @@ jobs: run: forge install - name: Build project - run: forge build + run: FOUNDRY_PROFILE=optimized-build forge build - name: Run tests - run: FOUNDRY_PROFILE=deep forge test -vvv + run: FOUNDRY_PROFILE=optimized-test-deep forge test -vvv - test-lite: - name: Run Forge Tests [lite build] + test-default: + name: Run Forge Tests (default) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -85,7 +85,7 @@ jobs: run: forge install - name: Build project - run: FOUNDRY_PROFILE=lite forge build + run: forge build - name: Run tests - run: FOUNDRY_PROFILE=lite forge test -vvv + run: forge test -vvv diff --git a/.gitignore b/.gitignore index 3a109388..3189d156 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ # Foundry build and cache directories out/ +out-optimized/ cache/ node_modules/ + +# Coverage +report/ +lcov.info diff --git a/README.md b/README.md index 70657779..fb3e9dbc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ERC-6900 Ref Implementation +# ERC-6900 Reference Implementation Reference implementation for [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900). It is an early draft implementation. @@ -13,20 +13,18 @@ The implementation includes an upgradable modular account with two plugins (`Sin Anyone is welcome to submit feedback and/or PRs to improve code or add Plugins. -### Build +### Testing + +The default Foundry profile can be used to compile (without IR) and test the entire project. The default profile should be used when generating coverage and debugging. ```bash forge build - -# or use the lite profile to reduce compilation time -FOUNDRY_PROFILE=lite forge build +forge test -vvv ``` -### Test +Since IR compilation generates different bytecode, it's useful to test against the contracts compiled via IR. Since compiling the entire project (including the test suite) takes a long time, special profiles can be used to precompile just the source contracts, and have the tests deploy the relevant contracts using those artifacts. ```bash -forge test -vvv - -# or use the lite profile to reduce compilation time -FOUNDRY_PROFILE=lite forge test -vvv +FOUNDRY_PROFILE=optimized-build forge build +FOUNDRY_PROFILE=optimized-test forge test -vvv ``` diff --git a/foundry.toml b/foundry.toml index a5d071fe..2754a5b2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,13 +1,16 @@ [profile.default] solc = '0.8.19' -via_ir = true +via_ir = false src = 'src' -out = 'out' test = 'test' libs = ['lib'] +out = 'out' optimizer = true optimizer_runs = 10_000 ignored_error_codes = [3628] +fs_permissions = [ + { access = "read", path = "./out-optimized" } +] [fuzz] runs = 500 @@ -17,12 +20,23 @@ runs=500 fail_on_revert = true depth = 10 -[profile.lite] -solc = '0.8.19' -via_ir = false -optimizer = true -optimizer_runs = 10_000 -ignored_error_codes = [3628] +[profile.optimized-build] +via_ir = true +test = 'src' +out = 'out-optimized' + +[profile.optimized-test] +src = 'test' + +[profile.optimized-test-deep] +src = 'test' + +[profile.optimized-test-deep.fuzz] +runs = 10000 + +[profile.optimized-test-deep.invariant] +runs = 5000 +depth = 32 [profile.deep.fuzz] runs = 10000 @@ -43,4 +57,4 @@ goerli = "${RPC_URL_GOERLI}" mainnet = { key = "${ETHERSCAN_API_KEY}" } goerli = { key = "${ETHERSCAN_API_KEY}" } -# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file +# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/test/TestUtils.sol b/test/TestUtils.sol deleted file mode 100644 index 26631b16..00000000 --- a/test/TestUtils.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import {Test, console} from "forge-std/Test.sol"; - -contract TestUtils is Test { - function printStorageReadsAndWrites(address addr) internal { - (bytes32[] memory accountReads, bytes32[] memory accountWrites) = vm.accesses(addr); - for (uint256 i = 0; i < accountWrites.length; i++) { - bytes32 valWritten = vm.load(addr, accountWrites[i]); - console.log( - string.concat("write loc: ", vm.toString(accountWrites[i]), " val: ", vm.toString(valWritten)) - ); - } - - for (uint256 i = 0; i < accountReads.length; i++) { - bytes32 valRead = vm.load(addr, accountReads[i]); - console.log(string.concat("read: ", vm.toString(accountReads[i]), " val: ", vm.toString(valRead))); - } - } -} diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 31d08bb1..0f016895 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test} from "forge-std/Test.sol"; - import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; @@ -23,8 +21,9 @@ import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/Funct import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; import {ComprehensivePlugin} from "../mocks/plugins/ComprehensivePlugin.sol"; import {MockPlugin} from "../mocks/MockPlugin.sol"; +import {OptimizedTest} from "../utils/OptimizedTest.sol"; -contract AccountLoupeTest is Test { +contract AccountLoupeTest is OptimizedTest { EntryPoint public entryPoint; SingleOwnerPlugin public singleOwnerPlugin; MSCAFactoryFixture public factory; @@ -40,7 +39,7 @@ contract AccountLoupeTest is Test { function setUp() public { entryPoint = new EntryPoint(); - singleOwnerPlugin = new SingleOwnerPlugin(); + singleOwnerPlugin = _deploySingleOwnerPlugin(); factory = new MSCAFactoryFixture(entryPoint, singleOwnerPlugin); comprehensivePlugin = new ComprehensivePlugin(); diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index dc5c350f..d2772a95 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test} from "forge-std/Test.sol"; - import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; @@ -17,9 +15,10 @@ import { ResultConsumerPlugin } from "../mocks/plugins/ReturnDataPluginMocks.sol"; import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; +import {OptimizedTest} from "../utils/OptimizedTest.sol"; // Tests all the different ways that return data can be read from plugins through an account -contract AccountReturnDataTest is Test { +contract AccountReturnDataTest is OptimizedTest { EntryPoint public entryPoint; // Just to be able to construct the factory SingleOwnerPlugin public singleOwnerPlugin; MSCAFactoryFixture public factory; @@ -32,7 +31,7 @@ contract AccountReturnDataTest is Test { function setUp() public { entryPoint = new EntryPoint(); - singleOwnerPlugin = new SingleOwnerPlugin(); + singleOwnerPlugin = _deploySingleOwnerPlugin(); factory = new MSCAFactoryFixture(entryPoint, singleOwnerPlugin); regularResultContract = new RegularResultContract(); diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index 82e5d027..48b65d98 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test, console} from "forge-std/Test.sol"; +import {console} from "forge-std/Test.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; @@ -11,18 +11,17 @@ import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; - import {Counter} from "../mocks/Counter.sol"; import {ResultCreatorPlugin} from "../mocks/plugins/ReturnDataPluginMocks.sol"; - import { EFPCallerPlugin, EFPCallerPluginAnyExternal, EFPPermittedCallHookPlugin, EFPExternalPermittedCallHookPlugin } from "../mocks/plugins/ExecFromPluginPermissionsMocks.sol"; +import {OptimizedTest} from "../utils/OptimizedTest.sol"; -contract ExecuteFromPluginPermissionsTest is Test { +contract ExecuteFromPluginPermissionsTest is OptimizedTest { Counter public counter1; Counter public counter2; Counter public counter3; @@ -47,7 +46,7 @@ contract ExecuteFromPluginPermissionsTest is Test { // Initialize the contracts needed to use the account. entryPoint = new EntryPoint(); - singleOwnerPlugin = new SingleOwnerPlugin(); + singleOwnerPlugin = _deploySingleOwnerPlugin(); factory = new MSCAFactoryFixture(entryPoint, singleOwnerPlugin); // Initialize the EFP caller plugins, which will attempt to use the permissions system to authorize calls. diff --git a/test/account/ManifestValidity.t.sol b/test/account/ManifestValidity.t.sol index 08de7f63..18972d07 100644 --- a/test/account/ManifestValidity.t.sol +++ b/test/account/ManifestValidity.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test} from "forge-std/Test.sol"; - import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; @@ -22,8 +20,9 @@ import { BadHookMagicValue_RuntimeValidationFunction_Plugin, BadHookMagicValue_PostExecHook_Plugin } from "../mocks/plugins/ManifestValidityMocks.sol"; +import {OptimizedTest} from "../utils/OptimizedTest.sol"; -contract ManifestValidityTest is Test { +contract ManifestValidityTest is OptimizedTest { EntryPoint public entryPoint; // Just to be able to construct the factory SingleOwnerPlugin public singleOwnerPlugin; MSCAFactoryFixture public factory; @@ -32,7 +31,7 @@ contract ManifestValidityTest is Test { function setUp() public { entryPoint = new EntryPoint(); - singleOwnerPlugin = new SingleOwnerPlugin(); + singleOwnerPlugin = _deploySingleOwnerPlugin(); factory = new MSCAFactoryFixture(entryPoint, singleOwnerPlugin); // Create an account with "this" as the owner, so we can execute along the runtime path with regular diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index 874b1e98..528eedd2 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test, console} from "forge-std/Test.sol"; +import {console} from "forge-std/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; @@ -23,8 +23,9 @@ import {Counter} from "../mocks/Counter.sol"; import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; import {ComprehensivePlugin} from "../mocks/plugins/ComprehensivePlugin.sol"; import {MockPlugin} from "../mocks/MockPlugin.sol"; +import {OptimizedTest} from "../utils/OptimizedTest.sol"; -contract UpgradeableModularAccountTest is Test { +contract UpgradeableModularAccountTest is OptimizedTest { using ECDSA for bytes32; EntryPoint public entryPoint; @@ -68,8 +69,8 @@ contract UpgradeableModularAccountTest is Test { beneficiary = payable(makeAddr("beneficiary")); vm.deal(beneficiary, 1 wei); - singleOwnerPlugin = new SingleOwnerPlugin(); - tokenReceiverPlugin = new TokenReceiverPlugin(); + singleOwnerPlugin = _deploySingleOwnerPlugin(); + tokenReceiverPlugin = _deployTokenReceiverPlugin(); factory = new MSCAFactoryFixture(entryPoint, singleOwnerPlugin); // Compute counterfactual address diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index fa2a8390..b33f5187 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test} from "forge-std/Test.sol"; - import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; @@ -18,8 +16,9 @@ import { MockUserOpValidation2HookPlugin, MockUserOpValidationPlugin } from "../mocks/plugins/ValidationPluginMocks.sol"; +import {OptimizedTest} from "../utils/OptimizedTest.sol"; -contract ValidationIntersectionTest is Test { +contract ValidationIntersectionTest is OptimizedTest { uint256 internal constant _SIG_VALIDATION_FAILED = 1; EntryPoint public entryPoint; @@ -35,7 +34,7 @@ contract ValidationIntersectionTest is Test { entryPoint = new EntryPoint(); owner1 = makeAddr("owner1"); - SingleOwnerPlugin singleOwnerPlugin = new SingleOwnerPlugin(); + SingleOwnerPlugin singleOwnerPlugin = _deploySingleOwnerPlugin(); MSCAFactoryFixture factory = new MSCAFactoryFixture(entryPoint, singleOwnerPlugin); account1 = factory.createAccount(owner1, 0); diff --git a/test/mocks/MSCAFactoryFixture.sol b/test/mocks/MSCAFactoryFixture.sol index f920eaea..03b90736 100644 --- a/test/mocks/MSCAFactoryFixture.sol +++ b/test/mocks/MSCAFactoryFixture.sol @@ -8,12 +8,14 @@ import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntry import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; +import {OptimizedTest} from "../utils/OptimizedTest.sol"; + /** * @title MSCAFactoryFixture * @dev a factory that initializes UpgradeableModularAccounts with a single plugin, SingleOwnerPlugin * intended for unit tests and local development, not for production. */ -contract MSCAFactoryFixture { +contract MSCAFactoryFixture is OptimizedTest { UpgradeableModularAccount public accountImplementation; SingleOwnerPlugin public singleOwnerPlugin; bytes32 private immutable _PROXY_BYTECODE_HASH; @@ -28,7 +30,7 @@ contract MSCAFactoryFixture { constructor(IEntryPoint _entryPoint, SingleOwnerPlugin _singleOwnerPlugin) { entryPoint = _entryPoint; - accountImplementation = new UpgradeableModularAccount(_entryPoint); + accountImplementation = _deployUpgradeableModularAccount(_entryPoint); _PROXY_BYTECODE_HASH = keccak256( abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(address(accountImplementation), "")) ); diff --git a/test/plugin/SingleOwnerPlugin.t.sol b/test/plugin/SingleOwnerPlugin.t.sol index d64d4a1d..090e5be1 100644 --- a/test/plugin/SingleOwnerPlugin.t.sol +++ b/test/plugin/SingleOwnerPlugin.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test} from "forge-std/Test.sol"; - import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; @@ -10,8 +8,9 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; import {ISingleOwnerPlugin} from "../../src/plugins/owner/ISingleOwnerPlugin.sol"; import {ContractOwner} from "../mocks/ContractOwner.sol"; +import {OptimizedTest} from "../utils/OptimizedTest.sol"; -contract SingleOwnerPluginTest is Test { +contract SingleOwnerPluginTest is OptimizedTest { using ECDSA for bytes32; SingleOwnerPlugin public plugin; @@ -29,7 +28,7 @@ contract SingleOwnerPluginTest is Test { event OwnershipTransferred(address indexed account, address indexed previousOwner, address indexed newOwner); function setUp() public { - plugin = new SingleOwnerPlugin(); + plugin = _deploySingleOwnerPlugin(); entryPoint = new EntryPoint(); a = makeAddr("a"); diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index e664e68b..009b42e9 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Test} from "forge-std/Test.sol"; - import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {ERC721PresetMinterPauserAutoId} from @@ -10,7 +8,6 @@ import {ERC721PresetMinterPauserAutoId} from import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; @@ -19,8 +16,9 @@ import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; import {MockERC777} from "../mocks/MockERC777.sol"; import {MockERC1155} from "../mocks/MockERC1155.sol"; +import {OptimizedTest} from "../utils/OptimizedTest.sol"; -contract TokenReceiverPluginTest is Test, IERC1155Receiver { +contract TokenReceiverPluginTest is OptimizedTest, IERC1155Receiver { UpgradeableModularAccount public acct; TokenReceiverPlugin public plugin; @@ -39,10 +37,10 @@ contract TokenReceiverPluginTest is Test, IERC1155Receiver { uint256 internal constant _BATCH_TOKEN_IDS = 5; function setUp() public { - MSCAFactoryFixture factory = new MSCAFactoryFixture(IEntryPoint(address(0)), new SingleOwnerPlugin()); + MSCAFactoryFixture factory = new MSCAFactoryFixture(IEntryPoint(address(0)), _deploySingleOwnerPlugin()); acct = factory.createAccount(address(this), 0); - plugin = new TokenReceiverPlugin(); + plugin = _deployTokenReceiverPlugin(); t0 = new ERC721PresetMinterPauserAutoId("t0", "t0", ""); t0.mint(address(this)); diff --git a/test/utils/OptimizedTest.sol b/test/utils/OptimizedTest.sol new file mode 100644 index 00000000..f9431acc --- /dev/null +++ b/test/utils/OptimizedTest.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import {Test} from "forge-std/Test.sol"; + +import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; + +import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; +import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; +import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; + +/// @dev This contract provides functions to deploy optimized (via IR) precompiled contracts. By compiling just +/// the source contracts (excluding the test suite) via IR, and using the resulting bytecode within the tests +/// (built without IR), we can avoid the significant overhead of compiling the entire test suite via IR. +/// +/// To use the optimized precompiled contracts, the project must first be built with the "optimized-build" profile +/// to populate the artifacts in the `out-optimized` directory. Then use the "optimized-test" or +/// "optimized-test-deep" profile to run the tests. +/// +/// To bypass this behavior for coverage or debugging, use the "default" profile. +abstract contract OptimizedTest is Test { + function _isOptimizedTest() internal returns (bool) { + string memory profile = vm.envOr("FOUNDRY_PROFILE", string("default")); + return _isStringEq(profile, "optimized-test-deep") || _isStringEq(profile, "optimized-test"); + } + + function _isStringEq(string memory a, string memory b) internal pure returns (bool) { + return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)); + } + + function _deployUpgradeableModularAccount(IEntryPoint entryPoint) + internal + returns (UpgradeableModularAccount) + { + return _isOptimizedTest() + ? UpgradeableModularAccount( + payable( + deployCode( + "out-optimized/UpgradeableModularAccount.sol/UpgradeableModularAccount.json", + abi.encode(entryPoint) + ) + ) + ) + : new UpgradeableModularAccount(entryPoint); + } + + function _deploySingleOwnerPlugin() internal returns (SingleOwnerPlugin) { + return _isOptimizedTest() + ? SingleOwnerPlugin(deployCode("out-optimized/SingleOwnerPlugin.sol/SingleOwnerPlugin.json")) + : new SingleOwnerPlugin(); + } + + function _deployTokenReceiverPlugin() internal returns (TokenReceiverPlugin) { + return _isOptimizedTest() + ? TokenReceiverPlugin(deployCode("out-optimized/TokenReceiverPlugin.sol/TokenReceiverPlugin.json")) + : new TokenReceiverPlugin(); + } +}