Break out Safe plugins to specific directories

Update plugins README.
Wrap deploy_all script's wallet with nonce manager.
This commit is contained in:
jacque006
2024-07-18 12:48:27 +02:00
parent 3a3c850989
commit baa419bfb6
27 changed files with 84 additions and 197 deletions

View File

@@ -72,7 +72,7 @@ jobs:
run: ./script/start.sh &
- name: Wait for bundler at port 3000
run: RPC_URL='localhost:3000' SLEEP_DURATION=1 ./script/wait-for-rpc.sh
run: RPC_URL='localhost:3000' SLEEP_DURATION_SECONDS=1 ./script/wait-for-rpc.sh
- name: Run hardhat integration tests
run: yarn hardhat test

View File

@@ -1,16 +1,27 @@
TODO link to other module bootstrapping repos, mention this is mainly safe
maybe ideas in here for cool new plugins to build?
one pager in hackmd or somewher else
- AA libs
- module bootstrapping
# Plugins
Please note, these plugins are in a pre-alpha state and are not ready for production use. In their current state, the plugins are meant for testing and experimentation.
Experimental plugins, modules, components for use with [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) (`v0.7.0`) compatible smart contract accounts. Most are currently built on top of Safe.
These plugins are in a pre-alpha state and are not ready for production use. They are intended for experimentation.
# Getting Started
This package is a hybrid Foundry & Hardhat project. Foundry is used for unit & integration testing, with Hardhat for end-to-end testing & examples of using a plugin in JavaScript/TypeScript runtime.
## When you should use this package
- If you are building a ERC-4337 plugin, module, or component and want end-to-end tooling & testing harnesses to make sure your it is compatible within the ERC-4337/Ethereum AA ecosystem. These can include but are not limited to:
- Validation
- Account Recovery
- Paymasters
- If you are building a module for an ERC-4437 Safe.
- If you are interested in experimenting with calldata compression. See also [the compression package](../compression/).
- If you have an interesting idea to integrate a novel cryptographic primitive or zero knowledge proof into the Ethereum AA ecosystem and want a place to start.
## When you shouldn't use this package
If you are developing a plugin/module for use with an ERC-4337 module spec, such as:
- ERC-7579, use https://docs.rhinestone.wtf/modulekit as a base instead.
- ERC-6900, use https://www.erc6900.io/build-a-plugin as a base instead.
## Getting Started
1. `cd packages/plugins`
2. Run `yarn submodules` to initialize git submodules
@@ -18,19 +29,19 @@ Please note, these plugins are in a pre-alpha state and are not ready for produc
4. Run `forge install` to install foundry dependencies
5. Run `cp .env.example .env` to create an `.env` file with the values from `.env.example`
## Build & generate Typechain definitions
### Build & generate Typechain definitions
```bash
yarn build
```
## Forge tests
### Forge tests
```bash
forge test --no-match-path test/unit/safe/SafeZkEmailRecoveryPlugin.t.sol -vvv
```
## Hardhat tests
### Hardhat tests
To run the hardhat tests, you'll need to run a geth node & bundler as some of them are integration tests:
@@ -47,3 +58,8 @@ To run the hardhat tests, you'll need to run a geth node & bundler as some of th
```bash
yarn hardhat test
```
### Things to keep in mind
- When writing Hardhat tests in the [e2e directory](./test/e2e/), DO NOT USE the `hre` ([Hardhat Runtime Environment](https://hardhat.org/hardhat-runner/docs/advanced/hardhat-runtime-environment)) when deploying contracts & interacting on chain. We do not use the normal internal Hardhat runtime as we need to run against a local geth instance & bundler to fully simulate the ERC-4337 flow end-to-end.
- Be mindful of the [opcode & storage limitations imposed by ERC-4337](https://eips.ethereum.org/EIPS/eip-7562).

View File

@@ -1,5 +1,11 @@
import fs from "fs/promises";
import { ethers } from "ethers";
import {
HDNodeWallet,
JsonRpcProvider,
NonceManager,
Signer,
Wallet,
} from "ethers";
import DeterministicDeployer, {
ContractFactoryConstructor,
DeployParams,
@@ -15,37 +21,38 @@ import {
SafeL2__factory,
Safe__factory,
EntryPoint__factory,
BLSSignatureAggregator__factory,
// BLSSignatureAggregator__factory,
BLSOpen__factory,
HandleOpsCaller__factory,
HandleAggregatedOpsCaller__factory,
// HandleAggregatedOpsCaller__factory,
AddressRegistry__factory,
} from "../typechain-types";
import makeDevFaster from "../test/e2e/utils/makeDevFaster";
import { TokenCallbackHandler__factory } from "../typechain-types/factories/lib/safe-contracts/contracts/handler/TokenCallbackHandler__factory";
import bundlerConfig from "./../config/bundler.config.json";
// import bundlerConfig from "./../config/bundler.config.json";
// 'test '.repeat(11) + 'absent'
const testAbsentAddress = "0xe8250207B79D7396631bb3aE38a7b457261ae0B6";
async function deploy() {
const { NODE_URL, MNEMONIC } = process.env;
const provider = new ethers.JsonRpcProvider(NODE_URL);
const provider = new JsonRpcProvider(NODE_URL);
await makeDevFaster(provider);
const hdNode = ethers.HDNodeWallet.fromPhrase(MNEMONIC!);
const wallet = new ethers.Wallet(hdNode.privateKey, provider);
const hdNode = HDNodeWallet.fromPhrase(MNEMONIC!);
const wallet = new Wallet(hdNode.privateKey, provider);
const nonceMgr = new NonceManager(wallet);
const recordingDeployer = await RecordingDeployer.init(wallet);
const recordingDeployer = await RecordingDeployer.init(nonceMgr);
const linkedAggregator = DeterministicDeployer.link(
BLSSignatureAggregator__factory,
[
{
"lib/account-abstraction/contracts/samples/bls/lib/BLSOpen.sol:BLSOpen":
recordingDeployer.deployer.calculateAddress(BLSOpen__factory, []),
},
],
);
// const linkedAggregator = DeterministicDeployer.link(
// BLSSignatureAggregator__factory,
// [
// {
// "lib/account-abstraction/contracts/samples/bls/lib/BLSOpen.sol:BLSOpen":
// recordingDeployer.deployer.calculateAddress(BLSOpen__factory, []),
// },
// ],
// );
const deployments = [
["SimulateTxAccessor", SimulateTxAccessor__factory],
@@ -117,9 +124,9 @@ class RecordingDeployer {
public safeDeployer: DeterministicDeployer,
) {}
static async init(wallet: ethers.Wallet): Promise<RecordingDeployer> {
const deployer = await DeterministicDeployer.init(wallet);
const safeDeployer = await DeterministicDeployer.initSafeVersion(wallet);
static async init(signer: Signer): Promise<RecordingDeployer> {
const deployer = await DeterministicDeployer.init(signer);
const safeDeployer = await DeterministicDeployer.initSafeVersion(signer);
try {
await fs.rename(

View File

@@ -1,7 +1,7 @@
#!/bin/bash
RPC_URL="${RPC_URL:=localhost:8545}"
SLEEP_DURATION_SECONDS="${SLEEP_DURATION:=0.1}"
SLEEP_DURATION_SECONDS="${SLEEP_DURATION_SECONDS:=0.1}"
max_tries=100
counter=0

View File

@@ -1 +0,0 @@
TODO break into sub directories of validators vs recovery modules, factories, etc.

View File

@@ -8,7 +8,7 @@ import {SafeProxy} from "safe-contracts/contracts/proxies/SafeProxy.sol";
import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";
import {SafeAnonAadhaarPlugin} from "./SafeAnonAadhaarPlugin.sol";
import {SafeAnonAadhaarPlugin} from "../validators/SafeAnonAadhaarPlugin.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE

View File

@@ -8,8 +8,8 @@ import {SafeProxy} from "safe-contracts/contracts/proxies/SafeProxy.sol";
import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";
import {SafeCompressionPlugin} from "./SafeCompressionPlugin.sol";
import {IDecompressor} from "../compression/decompressors/IDecompressor.sol";
import {SafeCompressionPlugin} from "../validators/SafeCompressionPlugin.sol";
import {IDecompressor} from "../../compression/decompressors/IDecompressor.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE

View File

@@ -8,7 +8,7 @@ import {SafeProxy} from "safe-contracts/contracts/proxies/SafeProxy.sol";
import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";
import {SafeECDSAPlugin} from "./SafeECDSAPlugin.sol";
import {SafeECDSAPlugin} from "../validators/SafeECDSAPlugin.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ISafe} from "./utils/Safe4337Base.sol";
import {ISafe} from "../utils/Safe4337Base.sol";
import {EmailAccountRecoveryRouter} from "./EmailAccountRecoveryRouter.sol";
import {EmailAccountRecovery} from "ether-email-auth/packages/contracts/src/EmailAccountRecovery.sol";

View File

@@ -2,9 +2,9 @@
pragma solidity >=0.7.0 <0.9.0;
pragma abicoder v2;
import {Safe4337Base, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
import {Safe4337Base, SIG_VALIDATION_FAILED} from "../utils/Safe4337Base.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/interfaces/IEntryPoint.sol";
import {IAnonAadhaar} from "./utils/anonAadhaar/interfaces/IAnonAadhaar.sol";
import {IAnonAadhaar} from "../utils/anonAadhaar/interfaces/IAnonAadhaar.sol";
interface ISafe {
function enableModule(address module) external;

View File

@@ -8,7 +8,7 @@ import {IEntryPoint, PackedUserOperation} from "account-abstraction/interfaces/I
import {BLS} from "account-abstraction/samples/bls/lib/hubble-contracts/contracts/libs/BLS.sol";
import {IBLSAccount} from "account-abstraction/samples/bls/IBLSAccount.sol";
import {Safe4337Base, ISafe} from "./utils/Safe4337Base.sol";
import {Safe4337Base, ISafe} from "../utils/Safe4337Base.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE

View File

@@ -9,9 +9,9 @@ import {BLS} from "account-abstraction/samples/bls/lib/hubble-contracts/contract
import {IBLSAccount} from "account-abstraction/samples/bls/IBLSAccount.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {Safe4337Base, ISafe, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
import {WaxLib as W} from "../compression/WaxLib.sol";
import {IDecompressor} from "../compression/decompressors/IDecompressor.sol";
import {Safe4337Base, ISafe, SIG_VALIDATION_FAILED} from "../utils/Safe4337Base.sol";
import {WaxLib as W} from "../../compression/WaxLib.sol";
import {IDecompressor} from "../../compression/decompressors/IDecompressor.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE

View File

@@ -2,7 +2,7 @@
pragma solidity >=0.7.0 <0.9.0;
pragma abicoder v2;
import {Safe4337Base, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
import {Safe4337Base, SIG_VALIDATION_FAILED} from "../utils/Safe4337Base.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/IEntryPoint.sol";

View File

@@ -4,9 +4,9 @@ pragma solidity >=0.8.0 <0.9.0;
import {HandlerContext} from "safe-contracts/contracts/handler/HandlerContext.sol";
import {BaseAccount} from "account-abstraction/core/BaseAccount.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/interfaces/IEntryPoint.sol";
import {WebAuthn} from "../primitives/WebAuthn.sol";
import {WebAuthn} from "../../primitives/WebAuthn.sol";
import {Safe4337Base, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
import {Safe4337Base, SIG_VALIDATION_FAILED} from "../utils/Safe4337Base.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE

View File

@@ -1,135 +0,0 @@
// import { ERC4337ZKPPasswordClient } from "@getwax/circuits";
import { expect } from "chai";
import { resolveProperties, ethers } from "ethers";
import sendUserOpAndWait from "./utils/sendUserOpAndWait";
import receiptOf from "./utils/receiptOf";
import {
MockGroth16Verifier__factory,
SafeZKPPasswordFactory__factory,
SafeZKPPasswordPlugin__factory,
} from "../../typechain-types";
import { setupTests } from "./utils/setupTests";
import { createUserOperation } from "./utils/createUserOp";
import { getUserOpHash } from "./utils/userOpUtils";
describe.skip("SafeZKPPasswordPlugin", () => {
it("should pass the ERC4337 validation", async () => {
const {
bundlerProvider,
provider,
admin,
owner,
entryPointAddress,
deployer,
safeSingleton,
} = await setupTests();
// const zkpClient = await ERC4337ZKPPasswordClient.create();
// Deploy zk password plugin
const safeZKPPasswordFactory = await deployer.connectOrDeploy(
SafeZKPPasswordFactory__factory,
[],
);
const signer = await provider.getSigner();
// TODO (merge-ok) Use real verifier from zkp dir
// https://github.com/getwax/wax/issues/143
const groth16Verifier = await new MockGroth16Verifier__factory(
signer,
).deploy();
const createArgs = [
safeSingleton,
entryPointAddress,
await owner.getAddress(),
0,
groth16Verifier,
] satisfies Parameters<typeof safeZKPPasswordFactory.create.staticCall>;
const accountAddress = await safeZKPPasswordFactory.create.staticCall(
...createArgs,
);
await receiptOf(safeZKPPasswordFactory.create(...createArgs));
// Native tokens for the pre-fund
await receiptOf(
admin.sendTransaction({
to: accountAddress,
value: ethers.parseEther("100"),
}),
);
// Construct userOp
const to = "0x42ef9B061d2B8416387FaA738Af7251668b0b142"; // Random address
const value = ethers.parseEther("1");
const data = "0x";
const userOpCallData =
SafeZKPPasswordPlugin__factory.createInterface().encodeFunctionData(
"execTransaction",
[to, value, data],
);
const encoder = ethers.AbiCoder.defaultAbiCoder();
const dummySignature = encoder.encode(
["uint256[2]", "uint256[2][2]", "uint256[2]"],
[
[0, 0],
[
[0, 0],
[0, 0],
],
[0, 0],
],
);
// Note: factoryParams is not used because we need to create both the safe
// proxy and the plugin, and 4337 currently only allows one contract
// creation in this step. Since we need an extra step anyway, it's simpler
// to do the whole create outside of 4337.
const factoryParams = {
factory: "0x",
factoryData: "0x",
};
const unsignedUserOperation = await createUserOperation(
provider,
bundlerProvider,
accountAddress,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
);
const resolvedUserOp = await resolveProperties(unsignedUserOperation);
const userOpHash = getUserOpHash(
resolvedUserOp,
entryPointAddress,
Number((await provider.getNetwork()).chainId),
);
const emojiPassword = "👻🎃🕸🦇🕷🪦";
// const { signature } = await zkpClient.proveUserOp(
// emojiPassword,
// userOpHash,
// );
const userOp = {
...unsignedUserOperation,
// signature,
};
const recipientBalanceBefore = await provider.getBalance(to);
// Send userOp
await sendUserOpAndWait(userOp, entryPointAddress, bundlerProvider);
const recipientBalanceAfter = await provider.getBalance(to);
const expectedRecipientBalance = recipientBalanceBefore + value;
expect(recipientBalanceAfter).to.equal(expectedRecipientBalance);
});
});

View File

@@ -4,8 +4,8 @@ pragma solidity ^0.8.12;
import "forge-std/Test.sol";
import "forge-std/console2.sol";
import {TestHelper} from "../../unit/utils/TestHelper.sol";
import {SafeZkEmailRecoveryPlugin, RecoveryRequest, GuardianRequest} from "../../../src/safe/SafeZkEmailRecoveryPlugin.sol";
import {IEmailAccountRecovery} from "../../../src/safe/EmailAccountRecoveryRouter.sol";
import {SafeZkEmailRecoveryPlugin, RecoveryRequest, GuardianRequest} from "../../../src/safe/recovery/SafeZkEmailRecoveryPlugin.sol";
import {IEmailAccountRecovery} from "../../../src/safe/recovery/EmailAccountRecoveryRouter.sol";
import {MockGroth16Verifier} from "../../../src/safe/utils/MockGroth16Verifier.sol";
import {Safe} from "safe-contracts/contracts/Safe.sol";
import {SafeProxy} from "safe-contracts/contracts/proxies/SafeProxy.sol";

View File

@@ -5,7 +5,7 @@ import "forge-std/Test.sol";
import "forge-std/console2.sol";
import {TestHelper} from "../utils/TestHelper.sol";
import {SafeBlsPluginHarness} from "../utils/SafeBlsPluginHarness.sol";
import {SafeBlsPlugin} from "../../../src/safe/SafeBlsPlugin.sol";
import {SafeBlsPlugin} from "../../../src/safe/validators/SafeBlsPlugin.sol";
import {Safe4337Base} from "../../../src/safe/utils/Safe4337Base.sol";
import {BLSSignatureAggregator} from "account-abstraction/samples/bls/BLSSignatureAggregator.sol";

View File

@@ -5,7 +5,7 @@ import "forge-std/Test.sol";
import "forge-std/console2.sol";
import {TestHelper} from "../utils/TestHelper.sol";
import {SafeECDSAPluginHarness} from "../utils/SafeECDSAPluginHarness.sol";
import {SafeECDSAPlugin} from "../../../src/safe/SafeECDSAPlugin.sol";
import {SafeECDSAPlugin} from "../../../src/safe/validators/SafeECDSAPlugin.sol";
import {Safe4337Base} from "../../../src/safe/utils/Safe4337Base.sol";
/* solhint-disable func-name-mixedcase */

View File

@@ -4,8 +4,8 @@ pragma solidity ^0.8.12;
import "forge-std/Test.sol";
import "forge-std/console2.sol";
import {TestHelper} from "../utils/TestHelper.sol";
import {SafeECDSARecoveryPlugin, ECDSARecoveryStorage} from "../../../src/safe/SafeECDSARecoveryPlugin.sol";
import {SafeECDSAPlugin} from "../../../src/safe/SafeECDSAPlugin.sol";
import {SafeECDSARecoveryPlugin, ECDSARecoveryStorage} from "../../../src/safe/recovery/SafeECDSARecoveryPlugin.sol";
import {SafeECDSAPlugin} from "../../../src/safe/validators/SafeECDSAPlugin.sol";
import {Safe} from "safe-contracts/contracts/Safe.sol";
import {SafeProxy} from "safe-contracts/contracts/proxies/SafeProxy.sol";
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";

View File

@@ -5,7 +5,7 @@ import "forge-std/Test.sol";
import "forge-std/console2.sol";
import {TestHelper} from "../utils/TestHelper.sol";
import {SafeWebAuthnPluginHarness} from "../utils/SafeWebAuthnPluginHarness.sol";
import {SafeWebAuthnPlugin} from "../../../src/safe/SafeWebAuthnPlugin.sol";
import {SafeWebAuthnPlugin} from "../../../src/safe/validators/SafeWebAuthnPlugin.sol";
import {Safe4337Base} from "../../../src/safe/utils/Safe4337Base.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/IEntryPoint.sol";

View File

@@ -4,7 +4,7 @@ pragma solidity ^0.8.12;
import "forge-std/Test.sol";
import "forge-std/console2.sol";
import {TestHelper} from "../utils/TestHelper.sol";
import {SafeZkEmailRecoveryPlugin, RecoveryRequest} from "../../../src/safe/SafeZkEmailRecoveryPlugin.sol";
import {SafeZkEmailRecoveryPlugin, RecoveryRequest} from "../../../src/safe/recovery/SafeZkEmailRecoveryPlugin.sol";
import {SafeZkEmailRecoveryPluginHarness} from "../utils/SafeZkEmailRecoveryPluginHarness.sol";
import {Safe} from "safe-contracts/contracts/Safe.sol";
import {SafeProxy} from "safe-contracts/contracts/proxies/SafeProxy.sol";

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {SafeBlsPlugin} from "../../../src/safe/SafeBlsPlugin.sol";
import {SafeBlsPlugin} from "../../../src/safe/validators/SafeBlsPlugin.sol";
/** Helper contract to expose internal functions for testing */
contract SafeBlsPluginHarness is SafeBlsPlugin {

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {SafeECDSAPlugin} from "../../../src/safe/SafeECDSAPlugin.sol";
import {SafeECDSAPlugin} from "../../../src/safe/validators/SafeECDSAPlugin.sol";
/** Helper contract to expose internal functions for testing */
contract SafeECDSAPluginHarness is SafeECDSAPlugin {

View File

@@ -2,7 +2,7 @@
pragma solidity ^0.8.12;
import {PackedUserOperation} from "account-abstraction/interfaces/IEntryPoint.sol";
import {SafeWebAuthnPlugin} from "../../../src/safe/SafeWebAuthnPlugin.sol";
import {SafeWebAuthnPlugin} from "../../../src/safe/validators/SafeWebAuthnPlugin.sol";
/** Helper contract to expose internal functions for testing */
contract SafeWebAuthnPluginHarness is SafeWebAuthnPlugin {

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {SafeZkEmailRecoveryPlugin} from "../../../src/safe/SafeZkEmailRecoveryPlugin.sol";
import {SafeZkEmailRecoveryPlugin} from "../../../src/safe/recovery/SafeZkEmailRecoveryPlugin.sol";
/** Helper contract to expose internal functions for testing */
contract SafeZkEmailRecoveryPluginHarness is SafeZkEmailRecoveryPlugin {