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

[DO NOT MERGE] Feat/deploy zksync #61

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
14 changes: 7 additions & 7 deletions Relayer.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ COPY packages/relayer/.env ./.env
# COPY packages/relayer/scripts/ ./scripts # FIXME: It's for testing
RUN cargo build --release

WORKDIR /email-wallet/packages/prover
RUN apt-get update && apt-get install -y python3.10 python3-distutils python3-pip python3-apt
RUN pip install modal
ARG modal_token_id
ARG modal_token_secret
RUN modal token set --token-id ${modal_token_id} --token-secret ${modal_token_secret}
RUN nohup modal serve modal_server.py &
# WORKDIR /email-wallet/packages/prover
# RUN apt-get update && apt-get install -y python3.10 python3-distutils python3-pip python3-apt
# RUN pip install modal
# ARG modal_token_id
# ARG modal_token_secret
# RUN modal token set --token-id ${modal_token_id} --token-secret ${modal_token_secret}
# RUN nohup modal serve modal_server.py &

WORKDIR /email-wallet/packages/relayer
CMD [ "/bin/bash", "-c", "/email-wallet/packages/relayer/scripts/startup.sh"]
Expand Down
146 changes: 146 additions & 0 deletions packages/contracts/deploy/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import * as dotenv from "dotenv";
dotenv.config();
import { deployContract, getWallet } from "./utils";
import * as hre from 'hardhat';
import * as ethers from 'ethers';
import * as zk from 'zksync-ethers';
import { Deployer } from '@matterlabs/hardhat-zksync-deploy';
import { HardhatRuntimeEnvironment } from "hardhat/types";
// An example of a basic deploy script
// It will deploy a Greeter contract to selected network
// as well as verify it on Block Explorer if possible for the network
export default async function () {

// TestToken
let testToken = await deployContract("TestERC20", ["TestToken", "TT"]);

// TokenRegistry
let tokenRegistryImpl = await deployContract("TokenRegistry");
let abi = new ethers.Interface(tokenRegistryImpl.interface.formatJson());
let data = abi.encodeFunctionData("initialize");
let proxy = await deployContract("ERC1967Proxy", [tokenRegistryImpl.target, data] )
let contractArtifact = await hre.artifacts.readArtifact("TokenRegistry");
const tokenRegistry = new ethers.Contract(
proxy,
contractArtifact.abi,
getWallet() // Interact with the contract on behalf of this wallet
);

// TODO: The below code is not working in inMemoryNode
// See https://github.com/matter-labs/hardhat-zksync/issues/714
let chainName = process.env.CHAIN_NAME as string;
let chainId = parseInt(process.env.CHAIN_ID as string);
console.log(`Setting chainId ${chainId} for chain ${chainName}`);
let tx = await tokenRegistry.setChainId(chainName, chainId);
await tx.wait();

let tokenName = process.env.TOKEN_NAME as string;
console.log(`Setting token name ${tokenName} for chain ${chainName}`);
tx = await tokenRegistry["setTokenAddress(uint256,string,address)"](chainId, tokenName, testToken.target);
await tx.wait();

let verifierImpl = await deployContract("AllVerifiers");

let signer = process.env.SIGNER as string;
let dkim = await deployContract("ECDSAOwnedDKIMRegistry", [signer]);

let weth = process.env.WETH as string;
let walletImpl = await deployContract("Wallet", [weth]);

let relayerHandlerImpl = await deployContract("RelayerHandler");
abi = new ethers.Interface(relayerHandlerImpl.interface.formatJson());
data = abi.encodeFunctionData("initialize");
proxy = await deployContract("ERC1967Proxy", [relayerHandlerImpl.target, data] )
contractArtifact = await hre.artifacts.readArtifact("RelayerHandler");
const relayerHandler = new ethers.Contract(
proxy,
contractArtifact.abi,
getWallet()
);

let extensionHandlerImpl = await deployContract("ExtensionHandler");
abi = new ethers.Interface(extensionHandlerImpl.interface.formatJson());
data = abi.encodeFunctionData("initialize");
proxy = await deployContract("ERC1967Proxy", [extensionHandlerImpl.target, data] )
contractArtifact = await hre.artifacts.readArtifact("ExtensionHandler");
const extensionHandler = new ethers.Contract(
proxy,
contractArtifact.abi,
getWallet()
);

let accountHandlerImpl = await deployContract("AccountHandler");
abi = new ethers.Interface(accountHandlerImpl.interface.formatJson());
let emailValidityDuration = 3600; // as same as `1 hours` in foundry script
data = abi.encodeFunctionData("initialize", [
await relayerHandler.getAddress(),
await dkim.getAddress(),
await verifierImpl.getAddress(),
await walletImpl.getAddress(),
emailValidityDuration
]);
proxy = await deployContract("ERC1967Proxy", [accountHandlerImpl.target, data] )
contractArtifact = await hre.artifacts.readArtifact("AccountHandler");
const accountHandler = new ethers.Contract(
proxy,
contractArtifact.abi,
getWallet()
);

let unclaimsHandlerImpl = await deployContract("UnclaimsHandler");
abi = new ethers.Interface(unclaimsHandlerImpl.interface.formatJson());
data = abi.encodeFunctionData("initialize", [
await relayerHandler.getAddress(),
await accountHandler.getAddress(),
await verifierImpl.getAddress(),
450000, // unclaimedFundClaimGas
500000, // unclaimedStateClaimGas
2592000, // unclaimsExpiryDuration = 30 days
2000000000, // maxFeePerGas = 2 gwei
]);
proxy = await deployContract("ERC1967Proxy", [unclaimsHandlerImpl.target, data] )
contractArtifact = await hre.artifacts.readArtifact("UnclaimsHandler");
const unclaimsHandler = new ethers.Contract(
proxy,
contractArtifact.abi,
getWallet()
);

let uniswapFactory = process.env.UNISWAP_FACTORY as string;
let oracle = await deployContract("UniswapTWAPOracle", [uniswapFactory, weth]);

let coreImpl = await deployContract("EmailWalletCore");
abi = new ethers.Interface(coreImpl.interface.formatJson());
data = abi.encodeFunctionData("initialize", [
await relayerHandler.getAddress(),
await accountHandler.getAddress(),
await unclaimsHandler.getAddress(),
await extensionHandler.getAddress(),
await verifierImpl.getAddress(),
await tokenRegistry.getAddress(),
await oracle.getAddress(),
await weth,
2000000000, // maxFeePerGas = 2 gwei
3600, // emailValidityDuration = 1 hour
450000, // unclaimedFundClaimGas
500000, // unclaimedStateClaimGas
]);
proxy = await deployContract("ERC1967Proxy", [coreImpl.target, data] )
contractArtifact = await hre.artifacts.readArtifact("EmailWalletCore");
const core = new ethers.Contract(
proxy,
contractArtifact.abi,
getWallet()
);

// TODO: The below code is not working in inMemoryNode
// See https://github.com/matter-labs/hardhat-zksync/issues/714
tx = await relayerHandler.transferOwnership(await core.getAddress());
await tx.wait();
tx = await accountHandler.transferOwnership(await core.getAddress());
await tx.wait();
tx = await unclaimsHandler.transferOwnership(await core.getAddress());
await tx.wait();
tx = await extensionHandler.transferOwnership(await core.getAddress());
await tx.wait();
}
168 changes: 168 additions & 0 deletions packages/contracts/deploy/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { Provider, Wallet } from "zksync-ethers";
import * as hre from "hardhat";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
import dotenv from "dotenv";
import { ethers } from "ethers";

import "@matterlabs/hardhat-zksync-node/dist/type-extensions";
import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions";

// Load env file
dotenv.config();

export const getProvider = () => {
const rpcUrl = hre.network.config.url;
if (!rpcUrl) throw `⛔️ RPC URL wasn't found in "${hre.network.name}"! Please add a "url" field to the network config in hardhat.config.ts`;

// Initialize zkSync Provider
const provider = new Provider(rpcUrl);

return provider;
}

export const getWallet = (privateKey?: string) => {
if (!privateKey) {
// Get wallet private key from .env file
if (!process.env.WALLET_PRIVATE_KEY) throw "⛔️ Wallet private key wasn't found in .env file!";
}

const provider = getProvider();

// Initialize zkSync Wallet
const wallet = new Wallet(privateKey ?? process.env.WALLET_PRIVATE_KEY!, provider);

return wallet;
}

export const verifyEnoughBalance = async (wallet: Wallet, amount: bigint) => {
// Check if the wallet has enough balance
const balance = await wallet.getBalance();
if (balance < amount) throw `⛔️ Wallet balance is too low! Required ${ethers.formatEther(amount)} ETH, but current ${wallet.address} balance is ${ethers.formatEther(balance)} ETH`;
}

/**
* @param {string} data.contract The contract's path and name. E.g., "contracts/Greeter.sol:Greeter"
*/
export const verifyContract = async (data: {
address: string,
contract: string,
constructorArguments: string,
bytecode: string
}) => {
const verificationRequestId: number = await hre.run("verify:verify", {
...data,
noCompile: true,
});
return verificationRequestId;
}

type DeployContractOptions = {
/**
* If true, the deployment process will not print any logs
*/
silent?: boolean
/**
* If true, the contract will not be verified on Block Explorer
*/
noVerify?: boolean
/**
* If specified, the contract will be deployed using this wallet
*/
wallet?: Wallet
}
export const deployContract = async (contractArtifactName: string, constructorArguments?: any[], options?: DeployContractOptions) => {
const log = (message: string) => {
if (!options?.silent) console.log(message);
}

log(`\nStarting deployment process of "${contractArtifactName}"...`);

const wallet = options?.wallet ?? getWallet();
const deployer = new Deployer(hre, wallet);
const artifact = await deployer.loadArtifact(contractArtifactName).catch((error) => {
if (error?.message?.includes(`Artifact for contract "${contractArtifactName}" not found.`)) {
console.error(error.message);
throw `⛔️ Please make sure you have compiled your contracts or specified the correct contract name!`;
} else {
throw error;
}
});

// Estimate contract deployment fee
const deploymentFee = await deployer.estimateDeployFee(artifact, constructorArguments || []);
log(`Estimated deployment cost: ${ethers.formatEther(deploymentFee)} ETH`);

// Check if the wallet has enough balance
await verifyEnoughBalance(wallet, deploymentFee);

// Deploy the contract to zkSync
const contract = await deployer.deploy(artifact, constructorArguments);
const address = await contract.getAddress();
const constructorArgs = contract.interface.encodeDeploy(constructorArguments);
const fullContractSource = `${artifact.sourceName}:${artifact.contractName}`;

// Display contract deployment info
log(`\n"${artifact.contractName}" was successfully deployed:`);
log(` - Contract address: ${address}`);
log(` - Contract source: ${fullContractSource}`);
log(` - Encoded constructor arguments: ${constructorArgs}\n`);

if (!options?.noVerify && hre.network.config.verifyURL) {
log(`Requesting contract verification...`);
await verifyContract({
address,
contract: fullContractSource,
constructorArguments: constructorArgs,
bytecode: artifact.bytecode,
});
}

return contract;
}

/**
* Rich wallets can be used for testing purposes.
* Available on zkSync In-memory node and Dockerized node.
*/
export const LOCAL_RICH_WALLETS = [
{
address: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
privateKey: "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"
},
{
address: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
privateKey: "0xac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3"
},
{
address: "0x0D43eB5B8a47bA8900d84AA36656c92024e9772e",
privateKey: "0xd293c684d884d56f8d6abd64fc76757d3664904e309a0645baf8522ab6366d9e"
},
{
address: "0xA13c10C0D5bd6f79041B9835c63f91de35A15883",
privateKey: "0x850683b40d4a740aa6e745f889a6fdc8327be76e122f5aba645a5b02d0248db8"
},
{
address: "0x8002cD98Cfb563492A6fB3E7C8243b7B9Ad4cc92",
privateKey: "0xf12e28c0eb1ef4ff90478f6805b68d63737b7f33abfa091601140805da450d93"
},
{
address: "0x4F9133D1d3F50011A6859807C837bdCB31Aaab13",
privateKey: "0xe667e57a9b8aaa6709e51ff7d093f1c5b73b63f9987e4ab4aa9a5c699e024ee8"
},
{
address: "0xbd29A1B981925B94eEc5c4F1125AF02a2Ec4d1cA",
privateKey: "0x28a574ab2de8a00364d5dd4b07c4f2f574ef7fcc2a86a197f65abaec836d1959"
},
{
address: "0xedB6F5B4aab3dD95C7806Af42881FF12BE7e9daa",
privateKey: "0x74d8b3a188f7260f67698eb44da07397a298df5427df681ef68c45b34b61f998"
},
{
address: "0xe706e60ab5Dc512C36A4646D719b889F398cbBcB",
privateKey: "0xbe79721778b48bcc679b78edac0ce48306a8578186ffcb9f2ee455ae6efeace1"
},
{
address: "0xE90E12261CCb0F3F7976Ae611A29e84a6A85f424",
privateKey: "0x3eb15da85647edd9a1159a4a13b9e7c56877c4eb33f614546d4db06a51868b1c"
}
]
57 changes: 57 additions & 0 deletions packages/contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { HardhatUserConfig } from "hardhat/config";
import "@matterlabs/hardhat-zksync-deploy";
import "@matterlabs/hardhat-zksync-solc";
import "@matterlabs/hardhat-zksync-verify";
import "@matterlabs/hardhat-zksync-node";

const config: HardhatUserConfig = {
defaultNetwork: "zkSyncSepoliaTestnet",
paths: {
// sources: "./contracts",
sources: "./src",
tests: "./test",
cache: "./cache",
artifacts: "./artifacts"
},
networks: {
zkSyncSepoliaTestnet: {
url: "https://sepolia.era.zksync.dev",
ethNetwork: "sepolia",
zksync: true,
verifyURL: "https://explorer.sepolia.era.zksync.dev/contract_verification",
},
zkSyncMainnet: {
url: "https://mainnet.era.zksync.io",
ethNetwork: "mainnet",
zksync: true,
verifyURL: "https://zksync2-mainnet-explorer.zksync.io/contract_verification",
},
inMemoryNode: {
url: "http://127.0.0.1:8011",
ethNetwork: "", // in-memory node doesn't support eth node; removing this line will cause an error
zksync: true,
},
hardhat: {
zksync: true,
},
},
zksolc: {
version: "latest",
// compilerSource: "binary",
settings: {
libraries: {
"src/libraries/DecimalUtils.sol": {
"DecimalUtils": "0x51eF809FFd89cf8056D4C17F0aFF1b6F8257EB60"
},
"src/libraries/SubjectUtils.sol": {
"SubjectUtils": "0x775f1fbfc46720025ACC2FFE652e578de642e6a2"
}
}
},
},
solidity: {
version: "0.8.23",
},
};

export default config;
Loading
Loading