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: support zkSyncEra and zkSyndTestnet chains #1259

Merged
merged 17 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ tsconfig.build.tsbuildinfo
/packages/smart-contracts/cache/
/packages/smart-contracts/types/
/packages/smart-contracts/src/types/
/packages/smart-contracts/build-zk/
/packages/smart-contracts/cache-zk/
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const chainId = 280;
1 change: 1 addition & 0 deletions packages/currency/src/chains/evm/data/zksync-era.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const chainId = 324;
4 changes: 4 additions & 0 deletions packages/currency/src/chains/evm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import * as RoninDefinition from './data/ronin';
import * as SokolDefinition from './data/sokol';
import * as TombchainDefinition from './data/tombchain';
import * as XDaiDefinition from './data/xdai';
import * as ZkSyncEraTestnetDefinition from './data/zksync-era-testnet';
import * as ZkSyncEraDefinition from './data/zksync-era';

export type EvmChain = Chain & {
chainId: number;
Expand Down Expand Up @@ -55,4 +57,6 @@ export const chains: Record<CurrencyTypes.EvmChainName, EvmChain> = {
sokol: SokolDefinition,
tombchain: TombchainDefinition,
xdai: XDaiDefinition,
zkSyncEraTestnet: ZkSyncEraTestnetDefinition,
zkSyncEra: ZkSyncEraDefinition,
};
12 changes: 12 additions & 0 deletions packages/currency/src/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ export const nativeCurrencies: Record<RequestLogicTypes.CURRENCY.ETH, NativeEthC
name: 'Core',
network: 'core',
},
{
symbol: 'zksync-ETH',
decimals: 18,
name: 'Ether',
network: 'zkSyncEra',
},
{
symbol: 'zksync-ETH-testnet',
decimals: 18,
name: 'Ether',
network: 'zkSyncEraTestnet',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why we don't keep the usual format (zk-sync-era-testnet) ?

},
],
[RequestLogicTypes.CURRENCY.BTC]: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const networks: Record<string, ethers.providers.Network> = {
mantle: { chainId: 5000, name: 'mantle' },
'mantle-testnet': { chainId: 5001, name: 'mantle-testnet' },
core: { chainId: 1116, name: 'core' },
zkSyncEraTestnet: { chainId: 280, name: 'zkSyncEraTestnet' },
zkSyncEra: { chainId: 324, name: 'zkSyncEra' },
};

/**
Expand Down Expand Up @@ -66,6 +68,10 @@ export class MultichainExplorerApiProvider extends ethers.providers.EtherscanPro
return 'https://explorer.testnet.mantle.xyz/api';
case 'core':
return 'https://openapi.coredao.org/';
case 'zkSyncEraTestnet':
return 'https://goerli.explorer.zksync.io/';
case 'zkSyncEra':
return 'https://explorer.zksync.io/';
default:
return super.getBaseUrl();
}
Expand Down
21 changes: 21 additions & 0 deletions packages/smart-contracts/deploy/deploy-zk-batch-contracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { erc20FeeProxyArtifact, ethereumFeeProxyArtifact } from '../src/lib';
import { deployContract } from './utils';
import * as hre from 'hardhat';
import { VMChainName } from 'types/dist/currency-types';
KolevDarko marked this conversation as resolved.
Show resolved Hide resolved

// 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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 () {
const [deployer] = await hre.ethers.getSigners();
const constructorArguments = [
erc20FeeProxyArtifact.getAddress(hre.network.name as VMChainName),
ethereumFeeProxyArtifact.getAddress(hre.network.name as VMChainName),
hre.ethers.constants.AddressZero,
hre.ethers.constants.AddressZero,
hre.ethers.constants.AddressZero,
deployer.address,
];
console.log(`Deploying BatchConversionPayments to zkSync ...`);
await deployContract('BatchConversionPayments', constructorArguments);
}
14 changes: 14 additions & 0 deletions packages/smart-contracts/deploy/deploy-zk-proxy-contracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { deployContract } from './utils';

// An example of a basic deploy script
KolevDarko marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove comment

// 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 () {
const deployList: string[] = ['ERC20FeeProxy', 'EthereumFeeProxy'];

for (let index = 0; index < deployList.length; index++) {
const contractName = deployList[index];
console.log(`Deploying ${contractName} to zkSync ...`);
await deployContract(contractName, []);
}
}
173 changes: 173 additions & 0 deletions packages/smart-contracts/deploy/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { Provider, Wallet, Contract } from 'zksync-web3';
import * as hre from 'hardhat';
import { Deployer } from '@matterlabs/hardhat-zksync-deploy';
import { formatEther } from 'ethers/lib/utils';
import { BigNumberish } from 'ethers';

import '@matterlabs/hardhat-zksync-node/dist/type-extensions';
import '@matterlabs/hardhat-zksync-verify/dist/src/type-extensions';
KolevDarko marked this conversation as resolved.
Show resolved Hide resolved
import { config } from 'dotenv';

config();

const accounts = process.env.DEPLOYMENT_PRIVATE_KEY
? [process.env.DEPLOYMENT_PRIVATE_KEY]
: process.env.DEPLOYER_MASTER_KEY
? [process.env.DEPLOYER_MASTER_KEY]
: process.env.ADMIN_PRIVATE_KEY
? [process.env.ADMIN_PRIVATE_KEY]
: undefined;

const WALLET_PRIVATE_KEY = (accounts || [])[0];

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);
KolevDarko marked this conversation as resolved.
Show resolved Hide resolved

return provider;
};

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

const provider = getProvider();

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

return wallet;
};

export const verifyEnoughBalance = async (wallet: Wallet, amount: BigNumberish) => {
// Check if the wallet has enough balance
const balance = await wallet.getBalance();
if (balance.lt(amount))
throw `⛔️ Wallet balance is too low! Required ${formatEther(amount)} ETH, but current ${
wallet.address
} balance is ${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 verifyContractByName = async (
contractArtifactName: string,
contractAddress: string,
) => {
const 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;
}
});

const fullContractSource = `${artifact.sourceName}:${artifact.contractName}`;

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

console.log(`Requesting contract verification...`);
await verifyContract({
address: contractAddress,
contract: fullContractSource,
constructorArguments: [],
bytecode: artifact.bytecode,
});
};

export const deployContract = async (
contractArtifactName: string,
constructorArguments?: any[],
options?: DeployContractOptions,
): Promise<Contract> => {
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: ${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 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: ${contract.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.address,
contract: fullContractSource,
constructorArguments: constructorArgs,
bytecode: artifact.bytecode,
});
}

return contract;
};
24 changes: 23 additions & 1 deletion packages/smart-contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import '@typechain/hardhat';
import '@nomiclabs/hardhat-waffle';
import '@nomicfoundation/hardhat-verify';
benjlevesque marked this conversation as resolved.
Show resolved Hide resolved
import '@nomiclabs/hardhat-ethers';

import '@matterlabs/hardhat-zksync-node';
import '@matterlabs/hardhat-zksync-deploy';
import '@matterlabs/hardhat-zksync-solc';
import '@matterlabs/hardhat-zksync-verify';

import { subtask, task } from 'hardhat/config';
import { config } from 'dotenv';
import deployAllContracts from './scripts/test-deploy-all';
Expand Down Expand Up @@ -161,6 +166,23 @@ export default {
chainId: 1116,
accounts,
},
zkSyncEraTestnet: {
url: 'https://testnet.era.zksync.dev',
ethNetwork: 'goerli',
zksync: true,
verifyURL: 'https://zksync2-testnet-explorer.zksync.dev/contract_verification',
accounts,
},
zkSyncEra: {
url: 'https://mainnet.era.zksync.io',
ethNetwork: 'mainnet',
zksync: true,
verifyURL: 'https://zksync2-mainnet-explorer.zksync.io/contract_verification',
accounts,
},
},
zksolc: {
version: '1.3.16',
},
etherscan: {
apiKey: {
Expand Down
8 changes: 7 additions & 1 deletion packages/smart-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
"tslib": "2.5.0"
},
"devDependencies": {
"@matterlabs/hardhat-zksync-deploy": "^0.6.5",
"@matterlabs/hardhat-zksync-node": "^0.0.1-beta.6",
"@matterlabs/hardhat-zksync-solc": "^0.4.2",
"@matterlabs/hardhat-zksync-verify": "^0.2.1",
"@matterlabs/zksync-contracts": "^0.6.1",
"@nomicfoundation/hardhat-verify": "2.0.0",
"@nomiclabs/hardhat-ethers": "2.0.2",
"@nomiclabs/hardhat-waffle": "2.0.1",
Expand All @@ -79,6 +84,7 @@
"shx": "0.3.2",
"solhint": "3.3.6",
"typechain": "5.1.1",
"web3": "1.7.3"
"web3": "1.7.3",
"zksync-web3": "^0.14.3"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export const batchConversionPaymentsArtifact = new ContractArtifact<BatchConvers
address: '0x3cF63891928B8CeebB81C95426600a18cd59C03f',
creationBlockNumber: 19856206,
},
// Caution: no ETHConversion, ERC20Conversion, and chainlinkConversionPath proxies on zkSyncEra
zkSyncEra: {
address: '0x0C41700ee1B363DB2ebC1a985f65cAf6eC4b1023',
creationBlockNumber: 19545614,
},
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export const batchPaymentsArtifact = new ContractArtifact<BatchPayments>(
address: '0x0DD57FFe83a53bCbd657e234B16A3e74fEDb8fBA',
creationBlockNumber: 35498688,
},
zkSyncEra: {
address: '0x0C41700ee1B363DB2ebC1a985f65cAf6eC4b1023',
creationBlockNumber: 19545614,
},
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,14 @@ export const erc20FeeProxyArtifact = new ContractArtifact<ERC20FeeProxy>(
address: '0x399F5EE127ce7432E4921a61b8CF52b0af52cbfE',
creationBlockNumber: 8317450,
},
zkSyncEraTestnet: {
address: '0xb4E10de047b72Af2a44F64892419d248d58d9dF5',
creationBlockNumber: 13616167,
},
zkSyncEra: {
address: '0x6e28Cc56C2E64c9250f39Cb134686C87dB196532',
creationBlockNumber: 19545285,
},
},
},
near: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ export const ethereumFeeProxyArtifact = new ContractArtifact<EthereumFeeProxy>(
address: '0xe11BF2fDA23bF0A98365e1A4c04A87C9339e8687',
creationBlockNumber: 8317446,
},
zkSyncEraTestnet: {
address: '0x0de6a1FB56a141086E0192269399af8b8a9e334A',
creationBlockNumber: 13616655,
},
zkSyncEra: {
address: '0xE9A708db0D30409e39810C44cA240fd15cdA9b1a',
creationBlockNumber: 19545294,
},
},
},
},
Expand Down
1 change: 1 addition & 0 deletions packages/smart-contracts/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"test/",
"scripts/",
"scripts-create2/",
"deploy/",
"hardhat.config.ts"
]
}
Loading