Merge pull request #257 from getwax/update-repo-docs

Update Repo Documentation, archive older components, CI e2e tests
This commit is contained in:
John Guilding
2024-07-20 16:32:16 +01:00
committed by GitHub
99 changed files with 194 additions and 1197 deletions

View File

@@ -1,4 +1,4 @@
name: packages/barebones
name: archive/barebones
on:
push:
@@ -6,11 +6,11 @@ on:
- main
pull_request:
paths:
- packages/barebones/**
- archive/barebones/**
defaults:
run:
working-directory: ./packages/barebones
working-directory: ./archive/barebones
env:
FOUNDRY_PROFILE: ci

View File

@@ -29,28 +29,6 @@ jobs:
with:
version: nightly
# Uncomment the lines below to install & generate zk assets
# - working-directory: ./packages/zkp/lib/circom
# run: cargo build --release
# - working-directory: ./packages/zkp/lib/circom
# run: cargo install --path circom
# - uses: actions/setup-node@v3
# with:
# node-version: "18.x"
# cache: "yarn"
# cache-dependency-path: packages/zkp/yarn.lock
# - working-directory: ./packages/zkp
# run: yarn install --frozen-lockfile
# - working-directory: ./packages/zkp
# run: yarn build
# - working-directory: ./packages/plugins
# run: yarn install --frozen-lockfile
- name: Run Forge build
run: |
forge --version
@@ -81,24 +59,20 @@ jobs:
cache: "yarn"
cache-dependency-path: packages/plugins/yarn.lock
# Uncommment the lines below to install & generate zk assets
# - working-directory: ./packages/zkp/lib/circom
# run: cargo build --release
# - working-directory: ./packages/zkp/lib/circom
# run: cargo install --path circom
# - working-directory: ./packages/zkp
# run: yarn install --frozen-lockfile
# - working-directory: ./packages/zkp
# run: yarn build
- name: Install Yarn dependencies
run: yarn install --frozen-lockfile
- name: Copy env file
run: cp .env.example .env
- name: Run hardhat compile
run: yarn hardhat compile
- name: Run hardhat build
run: yarn build
- name: Start geth node & bundler in background
run: ./script/start.sh &
- name: Wait for bundler at port 3000
run: RPC_URL='localhost:3000' SLEEP_DURATION_SECONDS=1 ./script/wait-for-rpc.sh
- name: Run hardhat integration tests
run: yarn hardhat test

11
.gitmodules vendored
View File

@@ -16,14 +16,11 @@
[submodule "packages/plugins/lib/account-abstraction"]
path = packages/plugins/lib/account-abstraction
url = https://github.com/eth-infinitism/account-abstraction
[submodule "packages/zkp/lib/circom"]
path = packages/zkp/lib/circom
url = https://github.com/iden3/circom.git
[submodule "packages/barebones/lib/openzeppelin-contracts"]
path = packages/barebones/lib/openzeppelin-contracts
[submodule "archive/barebones/lib/openzeppelin-contracts"]
path = archive/barebones/lib/openzeppelin-contracts
url = https://github.com/openzeppelin/openzeppelin-contracts
[submodule "packages/barebones/lib/forge-std"]
path = packages/barebones/lib/forge-std
[submodule "archive/barebones/lib/forge-std"]
path = archive/barebones/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "packages/plugins/lib/kernel"]
path = packages/plugins/lib/kernel

View File

@@ -1,36 +1,40 @@
# Wallet Account eXperiments (WAX)
WAX's goal is to deliver set of production-ready components that help developers easily utilise cryptographic primitives in smart accounts
We seek to achieve this with a three pronged approach:
1. Showcase, via examples, the benefits of cryptographic primitives in smart accounts
- including examples of novel use cases that compose smart account modules
2. Develop an easy-to-use opinionated SDK that makes integrating these benefits easy for non-web3 devs
3. Provide a focused dev environment for the integration of more primitives into smart accounts
![WAX Logo](./waxGreenLogo.png)
## Components **(diagram outdated)**
The showcase dApps are comprised of application specific code (pink) that leverages the SDK (yellow).
The SDK contains the off-chain companion of corresponding on-chain cryptographic primitives (grey).
Existing smart accounts will be used to demonstrate integration of the primitives in 4337-compatible verification logic.
**Diagram**: current explorations and proposed development
![WAX Composition](./docs/images/wax-composition.svg)
- The green areas are WAX's focus.
- Within the SDK there will be additions that enable projects to readily adopt novel features.
- On-chain, between smart contracts and primitives, the verification data+logic will be designed to be composable. Smart accounts can then dynamically benefit from more than one primitive, and this will be developed in sync with broader smart account modularisation.
Primary monorepo for the Wallet Account eXperiments (WAX) project, which focuses on integrating novel & useful cryptographic technologies into the Ethereum Account Abstraction (AA) ecosytem to improve the experience of using accounts. We build on top of [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) & related specs.
# Using this monorepo
## packages/demos
You're a web dev and want to bring the latest web3 capabilities to your product/users.
Here you will not only find 'hello world' wrappers of base primitives, but also composite examples highlighting novel product features.
Are you looking to build a plugin/module for the AA/ERC-4337 ecosystem and want examples & tooling? Check out [the plugins package](./packages/plugins/).
## packages/sdk
The sdk is useful for:
- non-web3 builders wanting to leverage the latest smart account capabiltiies
- SDK devs who also want to support interaction with the bleeding edge of smart account verification capabilities
Are you interested in compressing ERC-4337 data? Start with [the compression package](./packages/compression/)
## packages/plugins
You're a smart account dev and would like to integrate new primitives
New verification schemes are integrated into existing open smart accounts here.
This is where modularised byte verification is developed.
## Compression
[packages/compression](./packages//compression/)
Contracts implementing compression for ERC-4337 accounts to reduce data being posted from rollups.
## Demos
[packages/demos](./packages/demos/)
Demos showcasing WAX components.
## Deterministic Deployer
[packages/deterministic-deployer](./packages/deterministic-deployer/)
Deploys contracts to deterministic addresses.
## Plugins
[packages/plugins](./packages/plugins/)
Plugins/modules for smart contracts accounts, including test scaffolding.
## SDK
[packages/sdk](./packages/sdk/)
SDK code to help with ERC-4337 interactions.
## Archive
[archive](./archive/)
Work & projects that are no longer actively used but could be useful as references.

14
archive/README.md Normal file
View File

@@ -0,0 +1,14 @@
# Archive
This directory contains work & projects that are no longer active or maintained but could be useful as references
## Barebones
Lightweight Smart Account framework
## Images
Older technical diagrams for WAX project
## ZKP
Circuits related to ERC-4337 accounts & a TypeScript ZKEmail relayer

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -2,6 +2,8 @@
Based on https://github.com/privacy-scaling-explorations/zkp-app-boilerplate/tree/main/circuits
Includes a protoype [TypeScript ZKEmail relayer](./relayer/).
WARNING: These circuits are unsafe and are currently not recommended for production use.
## Required
@@ -11,17 +13,11 @@ WARNING: These circuits are unsafe and are currently not recommended for product
## Setup
**Step 1**: Download git submodules.
Install circom following [these instructions](https://docs.circom.io/getting-started/installation/)
```sh
git submodule update --init
```
The last working version was `v2.1.8`, [commit](https://github.com/iden3/circom/commit/f0deda416abe91e5dd906c55507c737cd9986ab5)
**Step 2**: Install circom.
Follow [these instructions](https://docs.circom.io/getting-started/installation/) replacing the git clone with `cd lib/circom`.
**Step 3**: Install NodeJS dependencies.
**Step 2**: Install NodeJS dependencies.
```sh
yarn
@@ -42,4 +38,4 @@ yarn test
## Other Commands
See [package.json](./package.json)
See [package.json](./package.json)

View File

@@ -1,25 +1,3 @@
# Packages
## Barebones
Lightweight Smart Account
## Compression
Contracts implementing compression for ERC-4337 accounts
## Demos
Demos showcasing WAX components
## Plugins
Plugins for Smart Accounts
## SDK
(Prototype) SDK code to help with ERC-4337 interactions
## ZKP
Circuits related to ERC-4337 accounts and a typescript relayer
See [top level README](../README.md)

View File

@@ -1,6 +1,8 @@
# Compression for 4337
# Compression for ERC-4337
Contracts implementing compression for 4337 wallets.
Contracts implementing compression for ERC-4337 accountss. This work is compatible with ERC-4337 v0.6.
See https://hackmd.io/@voltrevo/Bkz8syuUp for more details & performance.
## Performance

View File

@@ -1 +1,14 @@
# Demos
# Demos
Demos of WAX modules & functionality
[Email Recovery](./email-recovery/)
Demo of account recovery via email using https://github.com/zkemail/email-recovery
[Fee Calculator](./fee-calculator/)
Calculator to assist in determing if it is cheaper to use BLS + compression on rollups vs. a normal transaction/UserOp
[In Page](./email-recovery/)
Showcase of experimental modules bult by WAX team.

View File

@@ -0,0 +1,5 @@
# Determinisitic Deployer
Deploys contracts to deterministic addresses
Based on create2 and the deployer contract from https://github.com/Arachnid/deterministic-deployment-proxy

View File

@@ -1,8 +1,27 @@
# 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
@@ -10,21 +29,21 @@ 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 node and a bundler as some of them are integration tests:
To run the hardhat tests, you'll need to run a geth node & bundler as some of them are integration tests:
1. Start a geth node, fund accounts, deploy Safe contracts, and start a bundler:
@@ -39,3 +58,8 @@ To run the hardhat tests, you'll need to run a node and a bundler as some of the
```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

@@ -11,9 +11,6 @@
"build": "hardhat compile",
"lint": "eslint . --ext js,jsx,ts,tsx --report-unused-disable-directives --max-warnings 0"
},
"dependencies": {
"@getwax/circuits": "../zkp"
},
"devDependencies": {
"@account-abstraction/contracts": "0.7.0",
"@account-abstraction/utils": "^0.6.0",

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,6 +1,7 @@
#!/bin/bash
RPC_URL="${RPC_URL:=localhost:8545}"
SLEEP_DURATION_SECONDS="${SLEEP_DURATION_SECONDS:=0.1}"
max_tries=100
counter=0
@@ -14,7 +15,7 @@ function check_rpc() {
}
while ! check_rpc; do
sleep 0.1
sleep $SLEEP_DURATION_SECONDS
counter=$((counter + 1))
if (( counter >= max_tries )); then
echo "Error: Reached max_tries waiting for $RPC_URL to be available" >&2

View File

@@ -1,365 +0,0 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;
import {BasePlugin} from "erc6900-reference-implementation/plugins/BasePlugin.sol";
import {IPluginExecutor} from "erc6900-reference-implementation/interfaces/IPluginExecutor.sol";
import {ManifestFunction, ManifestAssociatedFunctionType, ManifestAssociatedFunction, PluginManifest, PluginMetadata, IPlugin} from "erc6900-reference-implementation/interfaces/IPlugin.sol";
import {MockGroth16Verifier} from "../safe/utils/MockGroth16Verifier.sol";
import {MockDKIMRegsitry} from "../safe/utils/MockDKIMRegsitry.sol";
import {IDKIMRegsitry} from "../safe/interface/IDKIMRegsitry.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE
//////////////////////////////////////////////////////////////////////////*/
struct RecoveryRequest {
bytes32 recoveryHash;
bytes32 dkimPublicKeyHash;
uint256 executeAfter;
address pendingNewOwner;
}
/// @title ZK Email Recovery Plugin
/// @author Wax
/// @notice This plugin recovers a ERC 6900 account via zk email guardians
contract ERC6900ZkEmailRecoveryModule is BasePlugin {
// metadata used by the pluginMetadata() method down below
string public constant NAME = "ZK Email Recovery Plugin";
string public constant VERSION = "1.0.0";
string public constant AUTHOR = "Wax";
// this is a constant used in the manifest, to reference our only dependency: the single owner plugin
// since it is the first, and only, plugin the index 0 will reference the single owner plugin
// we can use this to tell the modular account that we should use the single owner plugin to validate our user op
// in other words, we'll say "make sure the person calling the recovery functions is an owner of the account using our single plugin" // TODO: revisit this - recovery is more complicated as the owner is compromised
uint256
internal constant _MANIFEST_DEPENDENCY_INDEX_OWNER_USER_OP_VALIDATION =
0;
/** Default DKIM public key hashes registry */
IDKIMRegsitry public immutable defaultDkimRegistry;
/** verifier */
MockGroth16Verifier public immutable verifier;
/** Default delay has been set to a large timeframe on purpose. Please use a default delay suited to your specific context */
uint256 public constant defaultDelay = 2 weeks;
/** recovery hash domain */
bytes32 immutable RECOVERY_HASH_DOMAIN;
/** recovery request */
RecoveryRequest public recoveryRequest;
/** custom recovery delay */
uint256 public recoveryDelay;
/** dkim registry address */
address public dkimRegistry;
error RECOVERY_ALREADY_INITIATED();
error RECOVERY_NOT_CONFIGURED();
error INVALID_DKIM_KEY_HASH(
address account,
string emailDomain,
bytes32 dkimPublicKeyHash
);
error INVALID_PROOF();
error RECOVERY_NOT_INITIATED();
error DELAY_NOT_PASSED();
event RecoveryConfigured(
address indexed account,
address indexed owner,
bytes32 recoveryHash,
bytes32 dkimPublicKeyHash,
address dkimRegistry,
uint256 customDelay
);
event RecoveryInitiated(
address indexed account,
address newOwner,
uint256 executeAfter
);
event AccountRecovered(address indexed account, address newOwner);
event RecoveryCancelled(address indexed account);
event RecoveryDelaySet(address indexed account, uint256 delay);
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ Execution functions ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
/**
* @notice Initiates a recovery for an account using a zk email proof.
* @dev Rotates the account owner address to a new address. Uses the
* default delay period if no custom delay has been set. This is the second
* function that should be called in the recovery process - after configureRecovery
* @param newOwner The new owner address of the account
* @param emailDomain Domain name of the sender's email
* @param a Part of the proof
* @param b Part of the proof
* @param c Part of the proof
*/
function initiateRecovery(
address newOwner,
string memory emailDomain,
uint256[2] memory a,
uint256[2][2] memory b,
uint256[2] memory c
) external {
address account = msg.sender;
if (recoveryRequest.recoveryHash == bytes32(0)) {
revert RECOVERY_NOT_CONFIGURED();
}
if (recoveryRequest.executeAfter > 0) {
revert RECOVERY_ALREADY_INITIATED();
}
if (
!this.isDKIMPublicKeyHashValid(
emailDomain,
recoveryRequest.dkimPublicKeyHash
)
) {
revert INVALID_DKIM_KEY_HASH(
account,
emailDomain,
recoveryRequest.dkimPublicKeyHash
);
}
uint256[4] memory publicSignals = [
uint256(uint160(account)),
uint256(recoveryRequest.recoveryHash),
uint256(uint160(newOwner)),
uint256(recoveryRequest.dkimPublicKeyHash)
];
// verify proof
bool verified = verifier.verifyProof(a, b, c, publicSignals);
if (!verified) revert INVALID_PROOF();
uint256 executeAfter = block.timestamp + recoveryDelay;
recoveryRequest.executeAfter = executeAfter;
recoveryRequest.pendingNewOwner = newOwner;
emit RecoveryInitiated(account, newOwner, executeAfter);
}
/**
* @notice Recovers an account using a zk email proof.
* @dev Rotates the account owner address to a new address.
* This function is the third and final function that needs to be called in the
* recovery process. After configureRecovery & initiateRecovery
*/
function recoverAccount() public {
address account = msg.sender;
if (recoveryRequest.executeAfter == 0) {
revert RECOVERY_NOT_INITIATED();
}
if (block.timestamp > recoveryRequest.executeAfter) {
delete recoveryRequest;
// TODO: implement recovery logic for 6900 owner plugin
// owner = recoveryRequest.pendingNewOwner;
emit AccountRecovered(account, recoveryRequest.pendingNewOwner);
} else {
revert DELAY_NOT_PASSED();
}
}
/**
* @notice Cancels the recovery process of the sender if it exits.
* @dev Deletes the recovery request accociated with a account. Assumes
* the msg.sender is the account that the recovery request is being deleted for
*/
function cancelRecovery() external {
address account = msg.sender;
delete recoveryRequest;
emit RecoveryCancelled(account);
}
/**
* @notice Sets a custom delay for recovering the account.
* @dev Custom delay is used instead of the default delay when recovering the
* account. Custom delays should be configured with care as they can be
* used to bypass the default delay.
* @param delay The custom delay to be used when recovering the account
*/
function setRecoveryDelay(uint256 delay) external {
address account = msg.sender;
recoveryDelay = delay;
emit RecoveryDelaySet(account, delay);
}
/// @notice Return the DKIM public key hash for a given email domain and account address
/// @param emailDomain Email domain for which the DKIM public key hash is to be returned
function isDKIMPublicKeyHashValid(
string memory emailDomain,
bytes32 publicKeyHash
) public view returns (bool) {
if (dkimRegistry == address(0)) {
return
defaultDkimRegistry.isDKIMPublicKeyHashValid(
emailDomain,
publicKeyHash
);
} else {
return
IDKIMRegsitry(dkimRegistry).isDKIMPublicKeyHashValid(
emailDomain,
publicKeyHash
);
}
}
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ Plugin interface functions ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
/// @inheritdoc BasePlugin
function onInstall(bytes calldata data) external override {
(
bytes32 recoveryHash,
bytes32 dkimPublicKeyHash,
address _dkimRegistry,
uint256 customDelay
) = abi.decode(data, (bytes32, bytes32, address, uint256));
address account = msg.sender;
if (recoveryRequest.executeAfter > 0) {
revert RECOVERY_ALREADY_INITIATED();
}
if (customDelay > 0) {
recoveryDelay = customDelay;
} else {
recoveryDelay = defaultDelay;
}
recoveryRequest = RecoveryRequest({
recoveryHash: recoveryHash,
dkimPublicKeyHash: dkimPublicKeyHash,
executeAfter: 0,
pendingNewOwner: address(0)
});
dkimRegistry = _dkimRegistry; // FIXME: could be zero
emit RecoveryConfigured(
account,
msg.sender,
recoveryHash,
dkimPublicKeyHash,
dkimRegistry,
customDelay
);
}
/// @inheritdoc BasePlugin
function onUninstall(bytes calldata) external pure override {}
/// @inheritdoc BasePlugin
function pluginManifest()
external
pure
override
returns (PluginManifest memory)
{
PluginManifest memory manifest;
// since we are using the modular account, we will specify one depedency
// which will handle the user op validation for ownership
// you can find this depedency specified in the installPlugin call in the tests
manifest.dependencyInterfaceIds = new bytes4[](1);
manifest.dependencyInterfaceIds[0] = type(IPlugin).interfaceId;
// we have several execution functions that can be called, here we define
// those functions on the manifest as something that can be called during execution
manifest.executionFunctions = new bytes4[](5);
manifest.executionFunctions[0] = this.initiateRecovery.selector;
manifest.executionFunctions[1] = this.recoverAccount.selector;
manifest.executionFunctions[2] = this.cancelRecovery.selector;
manifest.executionFunctions[3] = this.setRecoveryDelay.selector;
manifest.executionFunctions[4] = this.isDKIMPublicKeyHashValid.selector;
// you can think of ManifestFunction as a reference to a function somewhere,
// we want to say "use this function" for some purpose - in this case,
// we'll be using the user op validation function from the single owner dependency
// and this is specified by the depdendency index
ManifestFunction
memory ownerUserOpValidationFunction = ManifestFunction({
functionType: ManifestAssociatedFunctionType.DEPENDENCY,
functionId: 0, // unused since it's a dependency
dependencyIndex: _MANIFEST_DEPENDENCY_INDEX_OWNER_USER_OP_VALIDATION
});
// here we will link together the recovery functions with the single owner user op validation
// this basically says "use this user op validation function and make sure everythings okay before calling the recovery functions"
// this will ensure that only an owner of the account can call the recovery functions
manifest.userOpValidationFunctions = new ManifestAssociatedFunction[](
1
);
manifest.userOpValidationFunctions[0] = ManifestAssociatedFunction({
executionSelector: this.initiateRecovery.selector,
associatedFunction: ownerUserOpValidationFunction
});
// TODO: recoverAccount should be permissionless if threshold is met
manifest.userOpValidationFunctions[1] = ManifestAssociatedFunction({
executionSelector: this.recoverAccount.selector,
associatedFunction: ownerUserOpValidationFunction
});
manifest.userOpValidationFunctions[2] = ManifestAssociatedFunction({
executionSelector: this.cancelRecovery.selector,
associatedFunction: ownerUserOpValidationFunction
});
manifest.userOpValidationFunctions[3] = ManifestAssociatedFunction({
executionSelector: this.setRecoveryDelay.selector,
associatedFunction: ownerUserOpValidationFunction
});
manifest.userOpValidationFunctions[4] = ManifestAssociatedFunction({
executionSelector: this.isDKIMPublicKeyHashValid.selector,
associatedFunction: ownerUserOpValidationFunction
});
// TODO: research best way to utilise this part of the manifest
// finally here we will always deny runtime calls to the initiateRecovery function as we will only call it through user ops
// this avoids a potential issue where a future plugin may define
// a runtime validation function for it and unauthorized calls may occur due to that
manifest.preRuntimeValidationHooks = new ManifestAssociatedFunction[](
1
);
manifest.preRuntimeValidationHooks[0] = ManifestAssociatedFunction({
executionSelector: this.initiateRecovery.selector,
associatedFunction: ManifestFunction({
functionType: ManifestAssociatedFunctionType
.PRE_HOOK_ALWAYS_DENY,
functionId: 0,
dependencyIndex: 0
})
});
return manifest;
}
/// @inheritdoc BasePlugin
function pluginMetadata()
external
pure
virtual
override
returns (PluginMetadata memory)
{
PluginMetadata memory metadata;
metadata.name = NAME;
metadata.version = VERSION;
metadata.author = AUTHOR;
return metadata;
}
}

View File

@@ -13,7 +13,8 @@ import {MockDKIMRegsitry} from "./safe/utils/MockDKIMRegsitry.sol";
import {IDKIMRegsitry} from "./safe/interface/IDKIMRegsitry.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE
THIS CONTRACT IS OUTDATED. NOT FOR PRODUCTION USE
It is recomended you use https://github.com/zkemail/email-recovery instead.
//////////////////////////////////////////////////////////////////////////*/
struct RecoveryRequest {

View File

@@ -1,276 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IValidator, VALIDATION_SUCCESS, VALIDATION_FAILED, MODULE_TYPE_VALIDATOR} from "erc7579-implementation/src/interfaces/IERC7579Module.sol";
import {ModeLib, ModeCode, CallType, CALLTYPE_SINGLE} from "erc7579-implementation/src/lib/ModeLib.sol";
import {IERC7579Account} from "erc7579-implementation/src/interfaces/IERC7579Account.sol";
import {ExecutionLib} from "erc7579-implementation/src/lib/ExecutionLib.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/IEntryPoint.sol";
import {ValidationData} from "account-abstraction/core/Helpers.sol";
import {MockGroth16Verifier} from "../safe/utils/MockGroth16Verifier.sol";
import {MockDKIMRegsitry} from "../safe/utils/MockDKIMRegsitry.sol";
import {IDKIMRegsitry} from "../safe/interface/IDKIMRegsitry.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE
//////////////////////////////////////////////////////////////////////////*/
struct RecoveryRequest {
bytes32 recoveryHash;
bytes32 dkimPublicKeyHash;
uint256 executeAfter;
address pendingNewOwner;
}
contract ERC7579ZkEmailRecoveryModule is IValidator {
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS & STORAGE
//////////////////////////////////////////////////////////////////////////*/
/** Default DKIM public key hashes registry */
IDKIMRegsitry public immutable defaultDkimRegistry;
/** verifier */
MockGroth16Verifier public immutable verifier;
/** Default delay has been set to a large timeframe on purpose. Please use a default
delay suited to your specific context */
uint256 public constant defaultDelay = 2 weeks;
/** recovery hash domain */
bytes32 immutable RECOVERY_HASH_DOMAIN;
/** recovery request */
RecoveryRequest public recoveryRequest;
/** custom recovery delay */
uint256 public recoveryDelay;
/** dkim registry address */
address public dkimRegistry;
mapping(address => bool) internal initialized;
error RECOVERY_ALREADY_INITIATED();
error RECOVERY_NOT_CONFIGURED();
error INVALID_DKIM_KEY_HASH(
address account,
string emailDomain,
bytes32 dkimPublicKeyHash
);
error INVALID_PROOF();
error RECOVERY_NOT_INITIATED();
error DELAY_NOT_PASSED();
error UNSUPPORTED_OPERATION();
event RecoveryConfigured(
address indexed account,
address indexed owner,
bytes32 recoveryHash,
bytes32 dkimPublicKeyHash,
address dkimRegistry,
uint256 customDelay
);
event RecoveryInitiated(
address indexed account,
address newOwner,
uint256 executeAfter
);
event AccountRecovered(address indexed account, address newOwner);
event RecoveryCancelled(address indexed account);
event RecoveryDelaySet(address indexed account, uint256 delay);
/*//////////////////////////////////////////////////////////////////////////
CONFIG
//////////////////////////////////////////////////////////////////////////*/
function onInstall(bytes calldata data) external override {
if (isInitialized(msg.sender)) revert AlreadyInitialized(msg.sender);
(
bytes32 recoveryHash,
bytes32 dkimPublicKeyHash,
address _dkimRegistry,
uint256 customDelay
) = abi.decode(data, (bytes32, bytes32, address, uint256));
address account = msg.sender;
if (recoveryRequest.executeAfter > 0) {
revert RECOVERY_ALREADY_INITIATED();
}
if (customDelay > 0) {
recoveryDelay = customDelay;
} else {
recoveryDelay = defaultDelay;
}
recoveryRequest = RecoveryRequest({
recoveryHash: recoveryHash,
dkimPublicKeyHash: dkimPublicKeyHash,
executeAfter: 0,
pendingNewOwner: address(0)
});
dkimRegistry = _dkimRegistry; // FIXME: could be zero
initialized[msg.sender] = true;
emit RecoveryConfigured(
account,
msg.sender,
recoveryHash,
dkimPublicKeyHash,
dkimRegistry,
customDelay
);
}
function onUninstall(bytes calldata) external override {
if (!isInitialized(msg.sender)) revert NotInitialized(msg.sender);
initialized[msg.sender] = false;
}
function isInitialized(address smartAccount) public view returns (bool) {
return initialized[smartAccount];
}
/*//////////////////////////////////////////////////////////////////////////
MODULE LOGIC
//////////////////////////////////////////////////////////////////////////*/
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) external override returns (uint256) {
address account = address(this);
(
address newOwner,
string memory emailDomain,
uint256[2] memory a,
uint256[2][2] memory b,
uint256[2] memory c
) = abi.decode(
userOp.signature,
(address, string, uint256[2], uint256[2][2], uint256[2])
);
// Check if the execution is allowed
bool isAllowedExecution;
bytes4 selector = bytes4(userOp.callData[0:4]);
if (selector == IERC7579Account.execute.selector) {
// Decode and check the execution
// Only single executions to installed validators are allowed
isAllowedExecution = _decodeAndCheckExecution(userOp.callData);
}
if (recoveryRequest.recoveryHash == bytes32(0)) {
return VALIDATION_FAILED;
}
if (recoveryRequest.executeAfter > 0) {
return VALIDATION_FAILED;
}
if (
!this.isDKIMPublicKeyHashValid(
emailDomain,
recoveryRequest.dkimPublicKeyHash
)
) {
return VALIDATION_FAILED;
}
uint256[4] memory publicSignals = [
uint256(uint160(account)),
uint256(recoveryRequest.recoveryHash),
uint256(uint160(newOwner)),
uint256(recoveryRequest.dkimPublicKeyHash)
];
// verify proof
bool verified = verifier.verifyProof(a, b, c, publicSignals);
if (!verified) return VALIDATION_FAILED;
uint256 executeAfter = block.timestamp + recoveryDelay;
recoveryRequest.executeAfter = executeAfter;
recoveryRequest.pendingNewOwner = newOwner;
emit RecoveryInitiated(account, newOwner, executeAfter);
return VALIDATION_SUCCESS;
}
function isValidSignatureWithSender(
address,
bytes32 hash,
bytes calldata data
) external view override returns (bytes4) {
// ERC-1271 not supported for recovery
revert UNSUPPORTED_OPERATION();
}
/// @notice Return the DKIM public key hash for a given email domain and account address
/// @param emailDomain Email domain for which the DKIM public key hash is to be returned
function isDKIMPublicKeyHashValid(
string memory emailDomain,
bytes32 publicKeyHash
) public view returns (bool) {
if (dkimRegistry == address(0)) {
return
defaultDkimRegistry.isDKIMPublicKeyHashValid(
emailDomain,
publicKeyHash
);
} else {
return
IDKIMRegsitry(dkimRegistry).isDKIMPublicKeyHashValid(
emailDomain,
publicKeyHash
);
}
}
/*//////////////////////////////////////////////////////////////////////////
INTERNAL
//////////////////////////////////////////////////////////////////////////*/
function _decodeAndCheckExecution(
bytes calldata callData
) internal returns (bool isAllowedExecution) {
// Get the mode and call type
ModeCode mode = ModeCode.wrap(bytes32(callData[4:36]));
CallType calltype = ModeLib.getCallType(mode);
if (calltype == CALLTYPE_SINGLE) {
// Decode the calldata
(address to, , ) = ExecutionLib.decodeSingle(callData[100:]);
// Check if the module is installed as a validator
return
IERC7579Account(msg.sender).isModuleInstalled(
MODULE_TYPE_VALIDATOR,
to,
""
);
} else {
return false;
}
}
/*//////////////////////////////////////////////////////////////////////////
METADATA
//////////////////////////////////////////////////////////////////////////*/
function name() external pure returns (string memory) {
return "SocialRecoveryValidator";
}
function version() external pure returns (string memory) {
return "0.0.1";
}
function isModuleType(uint256 typeID) external view returns (bool) {
return typeID == MODULE_TYPE_VALIDATOR;
}
}

View File

@@ -1,53 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
pragma abicoder v2;
import {Safe} from "safe-contracts/contracts/Safe.sol";
import {SafeProxyFactory} from "safe-contracts/contracts/proxies/SafeProxyFactory.sol";
import {SafeProxy} from "safe-contracts/contracts/proxies/SafeProxy.sol";
import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";
import {SafeZKPPasswordPlugin} from "./SafeZKPPasswordPlugin.sol";
import {IGroth16Verifier} from "./interface/IGroth16Verifier.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE
//////////////////////////////////////////////////////////////////////////*/
contract SafeZKPPasswordFactory {
function create(
Safe safeSingleton,
EntryPoint entryPoint,
address owner,
uint256 saltNonce,
IGroth16Verifier verifier
) external returns (SafeZKPPasswordPlugin) {
bytes32 salt = keccak256(abi.encodePacked(owner, saltNonce));
Safe safe = Safe(
payable(new SafeProxy{salt: salt}(address(safeSingleton)))
);
address[] memory owners = new address[](1);
owners[0] = owner;
SafeZKPPasswordPlugin plugin = new SafeZKPPasswordPlugin{salt: salt}(
address(entryPoint),
verifier
);
safe.setup(
owners,
1,
address(plugin),
abi.encodeCall(SafeZKPPasswordPlugin.enableMyself, (owner)),
address(plugin),
address(0),
0,
payable(address(0))
);
return SafeZKPPasswordPlugin(address(safe));
}
}

View File

@@ -1,114 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
pragma abicoder v2;
import {IGroth16Verifier} from "./interface/IGroth16Verifier.sol";
import {ISafe} from "./interface/ISafe.sol";
import {Safe4337Base, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/interfaces/IEntryPoint.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE
//////////////////////////////////////////////////////////////////////////*/
struct ZKPPasswordOwnerStorage {
address owner;
}
contract SafeZKPPasswordPlugin is Safe4337Base {
mapping(address => ZKPPasswordOwnerStorage) public zkpPasswordOwnerStorage;
address public immutable myAddress; // Module address
address private immutable _entryPoint;
IGroth16Verifier private immutable _verifier;
address internal constant _SENTINEL_MODULES = address(0x1);
event OWNER_UPDATED(
address indexed safe,
address indexed oldOwner,
address indexed newOwner
);
constructor(address entryPointAddress, IGroth16Verifier verifier) {
myAddress = address(this);
_entryPoint = entryPointAddress;
_verifier = verifier;
}
function getOwner(address safe) external view returns (address owner) {
owner = zkpPasswordOwnerStorage[safe].owner;
}
function execTransaction(
address to,
uint256 value,
bytes calldata data
) external payable {
_requireFromEntryPoint();
bool success = _currentSafe().execTransactionFromModule(
to,
value,
data,
0
);
require(success, "tx failed");
}
function enableMyself(address ownerKey) public {
// Called during safe setup as a delegatecall. This is why we use `this`
// to refer to the safe instead of `msg.sender` / _currentSafe().
ISafe(address(this)).enableModule(myAddress);
// Enable the safe address with the defined key
bytes memory _data = abi.encodePacked(ownerKey);
SafeZKPPasswordPlugin(myAddress).enable(_data);
}
function entryPoint() public view override returns (IEntryPoint) {
return IEntryPoint(_entryPoint);
}
function enable(bytes calldata _data) external payable {
address newOwner = address(bytes20(_data[0:20]));
address oldOwner = zkpPasswordOwnerStorage[msg.sender].owner;
zkpPasswordOwnerStorage[msg.sender].owner = newOwner;
emit OWNER_UPDATED(msg.sender, oldOwner, newOwner);
}
function _validateSignature(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal view override returns (uint256 validationData) {
// TODO (merge-ok) There is likely a more efficient way to encode this
// to save on space, which would be especially desirable on rollups.
uint256[2] memory a;
uint256[2][2] memory b;
uint256[2] memory c;
(a, b, c) = abi.decode(
userOp.signature,
(uint256[2], uint256[2][2], uint256[2])
);
uint256[1] memory pubSignals = [bytesToUint(userOpHash)];
bool result = _verifier.verifyProof(a, b, c, pubSignals);
if (!result) {
return SIG_VALIDATION_FAILED;
}
return 0;
}
// From https://ethereum.stackexchange.com/a/51234
function bytesToUint(bytes32 b) internal pure returns (uint256) {
uint256 number;
for (uint i = 0; i < b.length; i++) {
number =
number +
uint(uint8(b[i])) *
(2 ** (8 * (b.length - (i + 1))));
}
return number;
}
}

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,12 +1,13 @@
// 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";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE
THIS CONTRACT IS OUTDATED. NOT FOR PRODUCTION USE
It is recomended you use https://github.com/zkemail/email-recovery instead.
//////////////////////////////////////////////////////////////////////////*/
struct RecoveryRequest {
@@ -28,7 +29,7 @@ struct SafeAccountInfo {
/**
* A safe plugin that recovers a safe owner via a zkp of an email.
* NOT FOR PRODUCTION USE
* NOT FOR PRODUCTION USE, use https://github.com/zkemail/email-recovery instead
*/
contract SafeZkEmailRecoveryPlugin is EmailAccountRecovery {
/** Default delay has been set to a large timeframe on purpose. Please use a default delay suited to your specific context */

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 {

View File

@@ -846,14 +846,6 @@
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d"
integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==
"@getwax/circuits@../zkp":
version "0.0.1"
dependencies:
circomlib "2.0.5"
circomlibjs "0.1.7"
ethers "^6.11.1"
snarkjs "0.7.3"
"@humanwhocodes/module-importer@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
@@ -2316,13 +2308,6 @@ circom_runtime@0.1.21:
dependencies:
ffjavascript "0.2.56"
circom_runtime@0.1.24:
version "0.1.24"
resolved "https://registry.npmjs.org/circom_runtime/-/circom_runtime-0.1.24.tgz#60ca8a31c3675802fbab5a0bcdeb02556e510733"
integrity sha512-H7/7I2J/cBmRnZm9docOCGhfxzS61BEm4TMCWcrZGsWNBQhePNfQq88Oj2XpUfzmBTCd8pRvRb3Mvazt3TMrJw==
dependencies:
ffjavascript "0.2.60"
circom_runtime@0.1.25:
version "0.1.25"
resolved "https://registry.yarnpkg.com/circom_runtime/-/circom_runtime-0.1.25.tgz#62a33b371f4633f30238db7a326c43d988e3a170"
@@ -2330,11 +2315,6 @@ circom_runtime@0.1.25:
dependencies:
ffjavascript "0.3.0"
circomlib@2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/circomlib/-/circomlib-2.0.5.tgz#183c703e53ed7d011811842dbeeeb9819f4cc1d6"
integrity sha512-O7NQ8OS+J4eshBuoy36z/TwQU0YHw8W3zxZcs4hVwpEll3e4hDm3mgkIPqItN8FDeLEKZFK3YeT/+k8TiLF3/A==
circomlibjs@0.1.7, circomlibjs@^0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/circomlibjs/-/circomlibjs-0.1.7.tgz#9f5a7d9a23323744b11ee456b05b0cd81f48b554"
@@ -3215,19 +3195,6 @@ ethers@^5.5.3, ethers@^5.7.0:
"@ethersproject/web" "5.7.1"
"@ethersproject/wordlists" "5.7.0"
ethers@^6.11.1:
version "6.11.1"
resolved "https://registry.npmjs.org/ethers/-/ethers-6.11.1.tgz#96aae00b627c2e35f9b0a4d65c7ab658259ee6af"
integrity sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==
dependencies:
"@adraffy/ens-normalize" "1.10.1"
"@noble/curves" "1.2.0"
"@noble/hashes" "1.3.2"
"@types/node" "18.15.13"
aes-js "4.0.0-beta.5"
tslib "2.4.0"
ws "8.5.0"
ethers@^6.4.0, ethers@^6.7.0:
version "6.13.1"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.1.tgz#2b9f9c7455cde9d38b30fe6589972eb083652961"
@@ -3317,24 +3284,6 @@ ffjavascript@0.2.56:
wasmcurves "0.2.0"
web-worker "^1.2.0"
ffjavascript@0.2.60:
version "0.2.60"
resolved "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.2.60.tgz#4d8ae613d6bf4e98b3cc29ba10c626f5853854cf"
integrity sha512-T/9bnEL5xAZRDbQoEMf+pM9nrhK+C3JyZNmqiWub26EQorW7Jt+jR54gpqDhceA4Nj0YctPQwYnl8xa52/A26A==
dependencies:
wasmbuilder "0.0.16"
wasmcurves "0.2.2"
web-worker "^1.2.0"
ffjavascript@0.2.63:
version "0.2.63"
resolved "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.2.63.tgz#0c1216a1f123dc9181df69e144473704d2f115eb"
integrity sha512-dBgdsfGks58b66JnUZeZpGxdMIDQ4QsD3VYlRJyFVrKQHb2kJy4R2gufx5oetrTxXPT+aEjg0dOvOLg1N0on4A==
dependencies:
wasmbuilder "0.0.16"
wasmcurves "0.2.2"
web-worker "1.2.0"
ffjavascript@0.3.0, ffjavascript@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.3.0.tgz#442cd8fbb1ee4cbb1be9d26fd7b2951a1ea45d6a"
@@ -5165,16 +5114,6 @@ r1csfile@0.0.41:
fastfile "0.0.20"
ffjavascript "0.2.56"
r1csfile@0.0.47:
version "0.0.47"
resolved "https://registry.npmjs.org/r1csfile/-/r1csfile-0.0.47.tgz#ed95a0dc8e910e9c070253906f7a31bd8c5333c8"
integrity sha512-oI4mAwuh1WwuFg95eJDNDDL8hCaZkwnPuNZrQdLBWvDoRU7EG+L/MOHL7SwPW2Y+ZuYcTLpj3rBkgllBQZN/JA==
dependencies:
"@iden3/bigarray" "0.0.2"
"@iden3/binfileutils" "0.0.11"
fastfile "0.0.20"
ffjavascript "0.2.60"
r1csfile@0.0.48:
version "0.0.48"
resolved "https://registry.yarnpkg.com/r1csfile/-/r1csfile-0.0.48.tgz#a317fc75407a9da92631666c75bdfc13f0a7835a"
@@ -5584,22 +5523,6 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
snarkjs@0.7.3:
version "0.7.3"
resolved "https://registry.npmjs.org/snarkjs/-/snarkjs-0.7.3.tgz#7f703d05b810235255f2d0a70d8a9b8b3ea916e5"
integrity sha512-cDLpWqdqEJSCQNc+cXYX1XTKdUZBtYEisuOsgmXf/HUsN5WmGN+FO7HfCS+cMQT1Nzbm1a9gAEpKH6KRtDtS1Q==
dependencies:
"@iden3/binfileutils" "0.0.11"
bfj "^7.0.2"
blake2b-wasm "^2.4.0"
circom_runtime "0.1.24"
ejs "^3.1.6"
fastfile "0.0.20"
ffjavascript "0.2.63"
js-sha3 "^0.8.0"
logplease "^1.2.15"
r1csfile "0.0.47"
snarkjs@^0.7.3:
version "0.7.4"
resolved "https://registry.yarnpkg.com/snarkjs/-/snarkjs-0.7.4.tgz#b9ad5813f055ab84d33f1831a6f1f34a71b6cd46"
@@ -6443,11 +6366,6 @@ ws@8.17.1:
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
ws@8.5.0:
version "8.5.0"
resolved "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
ws@^7.4.6:
version "7.5.10"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9"

3
packages/sdk/README.md Normal file
View File

@@ -0,0 +1,3 @@
# SDK
Utilities to aid in ERC-4337 interactions & using WAX plugins.

6
packages/tools/README.md Normal file
View File

@@ -0,0 +1,6 @@
# Tools
## Fee Measurer
[fee-measurer](./fee-measurer/)
Facilitates direct measurement of L2 fees.

BIN
waxGreenLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB