Skip to content

Commit

Permalink
perf: precompile contracts for faster test runs
Browse files Browse the repository at this point in the history
  • Loading branch information
jaypaik committed Nov 29, 2023
1 parent 98bc1d7 commit dad76c9
Show file tree
Hide file tree
Showing 15 changed files with 131 additions and 103 deletions.
30 changes: 4 additions & 26 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: ERC6900 RI Test CI
name: ERC-6900 RI Test CI

on: [pull_request, workflow_dispatch]

Expand Down Expand Up @@ -45,7 +45,7 @@ jobs:

- name: "Lint the contracts"
run: "pnpm lint"

test:
name: Run Forge Tests
runs-on: ubuntu-latest
Expand All @@ -63,29 +63,7 @@ jobs:
run: forge install

- name: Build project
run: forge build

- name: Run tests
run: FOUNDRY_PROFILE=deep forge test -vvv

test-lite:
name: Run Forge Tests [lite build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Install forge dependencies
run: forge install

- name: Build project
run: FOUNDRY_PROFILE=lite forge build
run: FOUNDRY_PROFILE=optimized-build forge build --sizes

- name: Run tests
run: FOUNDRY_PROFILE=lite forge test -vvv
run: FOUNDRY_PROFILE=optimized-test-deep forge test -vvv
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Foundry build and cache directories
out/
out-optimized/
cache/
node_modules/

# Coverage
report/
lcov.info
18 changes: 8 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -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
```
33 changes: 24 additions & 9 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,12 +20,24 @@ 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'
bytecode_hash = 'none'

[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
Expand All @@ -43,4 +58,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
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
21 changes: 0 additions & 21 deletions test/TestUtils.sol

This file was deleted.

7 changes: 3 additions & 4 deletions test/account/AccountReturnData.t.sol
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand All @@ -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();
Expand Down
9 changes: 4 additions & 5 deletions test/account/ExecuteFromPluginPermissions.t.sol
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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;
Expand All @@ -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.
Expand Down
7 changes: 3 additions & 4 deletions test/account/ManifestValidity.t.sol
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand All @@ -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
Expand Down
7 changes: 3 additions & 4 deletions test/account/ModularAccountLoupe.t.sol
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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 ModularAccountLoupeTest is Test {
contract ModularAccountLoupeTest is OptimizedTest {
EntryPoint public entryPoint;
SingleOwnerPlugin public singleOwnerPlugin;
MSCAFactoryFixture public factory;
Expand All @@ -40,7 +39,7 @@ contract ModularAccountLoupeTest is Test {
function setUp() public {
entryPoint = new EntryPoint();

singleOwnerPlugin = new SingleOwnerPlugin();
singleOwnerPlugin = _deploySingleOwnerPlugin();
factory = new MSCAFactoryFixture(entryPoint, singleOwnerPlugin);
comprehensivePlugin = new ComprehensivePlugin();

Expand Down
9 changes: 5 additions & 4 deletions test/account/UpgradeableModularAccount.t.sol
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -63,8 +64,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
Expand Down
7 changes: 3 additions & 4 deletions test/account/ValidationIntersection.t.sol
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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;
Expand All @@ -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);
Expand Down
6 changes: 4 additions & 2 deletions test/mocks/MSCAFactoryFixture.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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), ""))
);
Expand Down
Loading

0 comments on commit dad76c9

Please sign in to comment.