Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/foundry zksync #21

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,6 @@ book

# Vs code settings
.vscode

# For zksync
zkout
5 changes: 5 additions & 0 deletions packages/contracts/.env.sample
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
LOCALHOST_RPC_URL=http://127.0.0.1:8545
SEPOLIA_RPC_URL=https://sepolia.base.org
# For zksync
# SEPOLIA_RPC_URL=https://sepolia.era.zksync.dev

MAINNET_RPC_URL=https://mainnet.base.org

# NEED 0x prefix
PRIVATE_KEY=

CHAIN_ID=84532
RPC_URL="https://sepolia.base.org"
SIGNER=0x69bec2dd161d6bbcc91ec32aa44d9333ebc864c0 # Signer for the dkim oracle on IC
Expand Down
145 changes: 143 additions & 2 deletions packages/contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ yarn install
```

## Requirements
- Newer than or equal to `forge 0.2.0 (b174c3a)`.
- Newer than or equal to `forge 0.2.0 (13497a5)`.

## Build and Test

Expand Down Expand Up @@ -190,4 +190,145 @@ It also provides the following entry functions with their default implementation
3. Let `uint templateId=keccak256(EMAIL_ACCOUNT_RECOVERY_VERSION_ID, "RECOVERY", templateIdx)`.
4. Assert that `templateId` is equal to `emailAuthMsg.templateId`.
5. Assert that `EmailAuth(guardian).authEmail(emailAuthMsg)` returns no error.
6. Call `processRecovery(guardian, templateIdx, emailAuthMsg.subjectParams, emailAuthMsg.proof.emailNullifier)`.
6. Call `processRecovery(guardian, templateIdx, emailAuthMsg.subjectParams, emailAuthMsg.proof.emailNullifier)`.

# For zkSync

You should use foundry-zksync, the installation process is following URL.
https://github.com/matter-labs/foundry-zksync

Current version foundry-zksync is forge 0.0.2 (13497a5 2024-05-16T00:24:48.304138000Z)
They can't use solc 0.8.25, so you should set appreciate solc version in foundry.toml.
For ex.

```
solc = "0.8.23"
```

Also the current foundry-zksync does not work correctly if your svm has 0.8.25 installed.
In that case, please do the following:

For Apple Silicon

```
rm -rf ~/Library/Application\ Support/svm/0.8.25
```

Or you can use docker too.

```
docker run -d -it -v $PWD:$PWD --name zksync-development --platform linux/amd64 ubuntu
docker exec -it zksync-development bash
```

In the docker container, you should execute following commands.

```
apt update
apt -y install git curl nodejs npm
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
curl -L https://foundry.paradigm.xyz | bash
source ~/.bashrc
git clone https://github.com/matter-labs/foundry-zksync.git
cd foundry-zksync
chmod +x ./install-foundry-zksync
./install-foundry-zksync
cd /Users/wataru_shinohara/GitHub/zkemail/ether-email-auth
yarn
cd packages/contracts
```

At the first forge build, you got the following warning.

```
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Warning: Your code or one of its dependencies uses the 'extcodesize' instruction, which is │
│ usually needed in the following cases: │
│ 1. To detect whether an address belongs to a smart contract. │
│ 2. To detect whether the deploy code execution has finished. │
│ zkSync Era comes with native account abstraction support (so accounts are smart contracts, │
│ including private-key controlled EOAs), and you should avoid differentiating between contracts │
│ and non-contract addresses. │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
--> ../../node_modules/forge-std/src/StdCheats.sol

Failed to compile with zksolc: Missing libraries detected [ZkMissingLibrary { contract_name: "SubjectUtils", contract_path: "src/libraries/SubjectUtils.sol", missing_libraries: ["src/libraries/DecimalUtils.sol:DecimalUtils"] }, ZkMissingLibrary { contract_name: "DecimalUtils", contract_path: "src/libraries/DecimalUtils.sol", missing_libraries: [] }]
```

Please run the following command in order to deploy the missing libraries:

```
forge create --deploy-missing-libraries --private-key <PRIVATE_KEY> --rpc-url <RPC_URL> --chain <CHAIN_ID> --zksync
forge create --deploy-missing-libraries --private-key {YOUR_PRIVATE_KEY} --rpc-url https://sepolia.era.zksync.dev --chain 300 --zksync
```

The above command output the following(for example):

```
[⠊] Compiling...
No files changed, compilation skipped
Deployer: 0xfB1CcCBDa2C41a77cDAC448641006Fc7fcf1f3b9
Deployed to: 0x91cc0f0A227b8dD56794f9391E8Af48B40420A0b
Transaction hash: 0x4f94ab71443d01988105540c3abb09ed66f8af5d0bb6a88691e2dafa88b3583d
[⠢] Compiling...
[⠃] Compiling 68 files with 0.8.23
[⠆] Solc 0.8.23 finished in 12.20s
Compiler run successful!
Deployer: 0xfB1CcCBDa2C41a77cDAC448641006Fc7fcf1f3b9
Deployed to: 0x981E3Df952358A57753C7B85dE7949Da4aBCf54A
Transaction hash: 0xfdca7b9eb3ae933ca123111489572427ee95eb6be74978b24c73fe74cb4988d7
```

After that, you can see the following line in foundry.toml.
Also, this line is needed only for foundry-zksync, if you use foundry, please remove this line. Otherwise, the test will fail.

```
libraries = ["{PROJECT_DIR}/packages/contracts/src/libraries/DecimalUtils.sol:DecimalUtils:{DEPLOYED_ADDRESS}", "{PROJECT_DIR}/packages/contracts/src/libraries/SubjectUtils.sol:SubjectUtils:{DEPLOYED_ADDRESS}"]

```

About Create2, `L2ContractHelper.computeCreate2Address` should be used.
And `type(ERC1967Proxy).creationCode` doesn't work correctly.
We need to hardcode the `type(ERC1967Proxy).creationCode` to bytecodeHash.
Perhaps that is different value in each compiler version.

You should replace the following line to the correct hash.
packages/contracts/src/EmailAccountRecovery.sol:L94

See, test/ComputeCreate2Address.t.sol

# For zkSync testing

Run `yarn zktest`.

Current foundry-zksync overrides the foundry behavior. If you installed foundry-zksync, some EVM code will be different and some test cases will be failed. If you want to test on other EVM, please install foundry.

Even if the contract size is fine for EVM, it may exceed the bytecode size limit for zksync, and the test may not be executed.
Therefore, EmailAccountRecovery.t.sol has been splited.

Currently some test cases are not work correctly because there is a issue about missing libraries.

Failing test cases are here.

- testAuthEmail()
- testExpectRevertAuthEmailEmailNullifierAlreadyUsed()
- testExpectRevertAuthEmailInvalidEmailProof()
- testExpectRevertAuthEmailInvalidSubject()
- testExpectRevertAuthEmailInvalidTimestamp()
- testIsValidSignature()
- testIsValidSignatureReturnsFalse()

# For integration testing

forge test --match-test 'testIntegration_Account_Recovery' --zksync --chain 300 -vvv --ffi

# For zkSync deployment (For test net)

You need to edit .env at first.
Second just run the following commands with `--zksync`

```
source .env
forge script script/DeployCommons.s.sol:Deploy --zksync --rpc-url $SEPOLIA_RPC_URL --broadcast -vvvv
```

14 changes: 13 additions & 1 deletion packages/contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,31 @@ optimizer-runs = 20_000
fs_permissions = [
{ access = "read", path = "./artifacts/WETH9.sol/WETH9.json" },
{ access = "read", path = "./test/build_integration" },
{ access = "read", path = "./zkout/ERC1967Proxy.sol/artifacts.json" },
]
solc = "0.8.23"

# See more config options https://github.com/foundry-rs/foundry/tree/master/config

# OpenZeppelin
build_info = true
extra_output = ["storageLayout"]

# For missing libraries, please comment out this if you use foundry
#libraries = ["{PROJECT_DIR}/packages/contracts/src/libraries/DecimalUtils.sol:DecimalUtils:0x91cc0f0a227b8dd56794f9391e8af48b40420a0b", "{PROJECT_DIR}/packages/contracts/src/libraries/SubjectUtils.sol:SubjectUtils:0x981e3df952358a57753c7b85de7949da4abcf54a"]

[rpc_endpoints]
localhost = "${LOCALHOST_RPC_URL}"
sepolia = "${SEPOLIA_RPC_URL}"
mainnet = "${MAINNET_RPC_URL}"

[etherscan]
sepolia = { key = "${ETHERSCAN_API_KEY}" }
mainnet = { key = "${ETHERSCAN_API_KEY}" }
mainnet = { key = "${ETHERSCAN_API_KEY}" }

[profile.zksync]
src = 'src'
libs = ["../../node_modules", "lib"]
fallback_oz = true
is_system = false
mode = "3"
7 changes: 5 additions & 2 deletions packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
"version": "1.0.0",
"license": "MIT",
"scripts": {
"build": "forge build --use 0.8.23",
"test": "forge test --no-match-test \"testIntegration\" --use 0.8.23",
"build": "forge build",
"zkbuild": "forge build --zksync",
"test": "forge test --no-match-test \"testIntegration\"",
"zktest": "forge test --no-match-test \"testIntegration\" --zksync --chain 300",
"lint": "solhint 'src/**/*.sol'"
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.0",
"@openzeppelin/contracts-upgradeable": "^5.0.0",
"@zk-email/contracts": "^6.0.0",
"@matterlabs/zksync-contracts": "^0.6.1",
"solady": "^0.0.123"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/contracts/remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@openzeppelin/contracts-upgradeable/=../../node_modules/@openzeppelin/contracts-upgradeable
@zk-email/=../../node_modules/@zk-email
@uniswap/=../../node_modules/@uniswap
@matterlabs/=../../node_modules/@matterlabs
forge-std/=../../node_modules/forge-std/src
ds-test/=../../node_modules/ds-test/src
solady/=../../node_modules/solady/src/
Expand Down
30 changes: 27 additions & 3 deletions packages/contracts/src/EmailAccountRecovery.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity ^0.8.12;
import "./EmailAuth.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {L2ContractHelper} from "@matterlabs/zksync-contracts/l2/contracts/L2ContractHelper.sol";


/// @title Email Account Recovery Contract
/// @notice Provides mechanisms for email-based account recovery, leveraging guardians and template-based email verification.
Expand Down Expand Up @@ -80,8 +82,27 @@ abstract contract EmailAccountRecovery {
function computeEmailAuthAddress(
bytes32 accountSalt
) public view returns (address) {
return
Create2.computeAddress(
// If on zksync, we use L2ContractHelper.computeCreate2Address
if(block.chainid == 324 || block.chainid == 300) {
// TODO: The bytecodeHash is hardcoded here because type(ERC1967Proxy).creationCode doesn't work on eraVM currently
// If you failed some test cases, check the bytecodeHash by yourself
// see, test/ComputeCreate2Address.t.sol
return L2ContractHelper.computeCreate2Address(
address(this),
accountSalt,
bytes32(0x010000830a636831d3678f83275e3c9257b482d6ee5dc76d741ced984134f9de),
keccak256(
abi.encode(
emailAuthImplementation(),
abi.encodeCall(
EmailAuth.initialize,
(address(this), accountSalt)
)
)
)
);
} else {
return Create2.computeAddress(
accountSalt,
keccak256(
abi.encodePacked(
Expand All @@ -96,6 +117,7 @@ abstract contract EmailAccountRecovery {
)
)
);
}
}

/// @notice Calculates a unique subject template ID for an acceptance subject template using its index.
Expand Down Expand Up @@ -137,7 +159,6 @@ abstract contract EmailAccountRecovery {
)
);
}

/// @notice Handles an acceptance by a new guardian.
/// @dev This function validates the email auth message, deploys a new EmailAuth contract as a proxy if validations pass and initializes the contract.
/// @param emailAuthMsg The email auth message for the email send from the guardian.
Expand Down Expand Up @@ -167,6 +188,8 @@ abstract contract EmailAccountRecovery {
(address(this), emailAuthMsg.proof.accountSalt)
)
);


EmailAuth guardianEmailAuth = EmailAuth(address(proxy));
guardianEmailAuth.updateDKIMRegistry(dkim());
guardianEmailAuth.updateVerifier(verifier());
Expand Down Expand Up @@ -207,6 +230,7 @@ abstract contract EmailAccountRecovery {
address guardian = computeEmailAuthAddress(
emailAuthMsg.proof.accountSalt
);
// Check if the guardian is deployed
require(address(guardian).code.length > 0, "guardian is not deployed");
uint templateId = uint256(
keccak256(
Expand Down
Loading
Loading