zkSync Era、Linea、Scroll合约部署避坑实录

post image

最近搞了个小项目,在各种L2链上进行部署,部署的过程中发现了各种坑,因此记录下来,也许可以帮助到一些有心人。

一、zkSync Era

zksync era部署合约和其他Evm网络不同,不能直接使用remix进行部署,官方出的解决方案是使用hardhat插件,但是,坑实在是太多了,为什么不提供Docker呢?这样是真的可以一劳永逸,node,npm的兼容性也是一言难尽,花了太多的时间。

很遗憾,本文也没有提供有效的Docker,因为我本地搭建好了,也许以后会有更新,望见谅。但是本文会比官网的doc更为细致,适合初学者使用。

本地环境:

  1. 系统:Centos7,内核5.4

  2. 使用yarn编译,yum update, yum install yarn即可

  3. node版本:v16.18.1

  4. npm版本:8.19.2

执行依赖安装:

创建项目文件夹,并进入后执行下面的命令
mkdir TestToken
cd TestToken
yarn add -D cpu-features typescript ts-node hardhat ethers@~5.7.2 zksync-web3@^0.14.3 @matterlabs/hardhat-zksync-solc @matterlabs/hardhat-zksync-deploy 

注意:这里多了个cpu-features,如果不先安装cpu-features,会报gpy安装失败

下载编译工具

从下面网址选择最新的最适合你cpu的编译工具,并把它保存在计算机的某个文件夹里,下面hardhat.config.js里面会用上,例如我下载的是:zksolc-linux-amd64-musl-v1.3.13,并放在/root/env/文件夹下面
https://github.com/matter-labs/zksolc-bin

给zksolc添加可执行权限:
chmod a+x zksolc-linux-amd64-musl-v1.3.13

创建空项目

npm init --yes

会在当前目录下面生成contracts文件夹,hardhat.config.js等文件

编辑hardhat.config.js文件

/** @type import('hardhat/config').HardhatUserConfig */
import "@matterlabs/hardhat-zksync-deploy";
import "@matterlabs/hardhat-zksync-solc";
import "@matterlabs/hardhat-zksync-verify";

module.exports = {
    zksolc: {
        version: "latest", // 默认即可,也可以填1.3.13,就是zksolc的版本
        compilerSource: "binary", // 默认即可
        settings: {
            compilerPath: "/root/env/zksolc-linux-amd64-musl-v1.3.13",  // 这里一定放之前下载的zksolc-linux-amd64-musl-v1.3.13路径
            libraries:{}, // 我没有使用任何library,如果有的话可能需要填,具体怎么填我也不知道
            isSystem: false, // optional.  Enables Yul instructions available only for zkSync system contracts and libraries
            forceEvmla: false, // optional. Falls back to EVM legacy assembly if there is a bug with Yul
            optimizer: { // 是否使用编译优化,一般都要使用的,因为这可以降低部署的gas
                enabled: true, // optional. True by default
                mode: '3' // optional. 3 by default, z to optimize bytecode size
            }
        },
    },
    defaultNetwork: "zkSyncTestnet", // 如果部署测试网,那么就填zkSyncTestnet,如果是主网,那么填zkSync,与下面networks中内容一一对应

    networks: {
        mainnet: {
            url: "https://mainnet.infura.io/v3/一串16进制字符", // 主网rpc节点,请填入自己在infura申请的goerli节点
            zksync: false, // 默认是false
        },
        goerli: {
            url: "https://goerli.infura.io/v3/一串16进制字符", // Goerli网rpc节点,请填入自己在infura申请的goerli节点
            zksync: false, // 默认是false
        },
        zkSyncTestnet: {
            url: "https://zksync2-testnet.zksync.dev", // zkSync测试网的节点
            ethNetwork: "goerli", // zkSync是L2需要有对应的L1,测试网对应goerli
            zksync: true, // 默认是true
            verifyURL: "https://zksync2-testnet-explorer.zksync.dev/contract_verification", // 这个是合约开源的验证URL
        },
        zkSync: {
            url: "https://mainnet.era.zksync.io", // zkSync正式网的节点
            ethNetwork: "mainnet", // zkSync是L2需要有对应的L1,era正式网对应mainnet
            zksync: true,  // 默认是true
            verifyURL: "https://zksync2-mainnet-explorer.zksync.io/contract_verification", // 这个是合约开源的验证URL
        }
    },
    solidity: {
        version: "0.8.20", // 合约编译的solidity版本号,这个一定要与sol合约文件的版本号一致
    },
};

编辑合约文件

合约文件比较简单,直接将你的合约文件全部丢到contracts文件夹中即可,注意合约文件的文件名,最好与你要部署的合约的class名保持一致,后面写Deploy文件时会比较简单直观一些

编辑Deploy文件

在项目根目录创建deploy文件夹:
mkdir deploy
cd deploy
vi deploy.ts

deploy.ts文件内容解说

import { Wallet, utils } from "zksync-web3";
import * as ethers from "ethers";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";

export default async function (hre: HardhatRuntimeEnvironment) {
  console.log(`Running deploy script for the Test contract`);

  // 初始化钱包 填入私钥
  const wallet = new Wallet("这里填私钥,不要带0x,有人说助记词怎么办?自行google吧,我也没用过");

  // 创建deployer
  const deployer = new Deployer(hre, wallet);
  // 设置部署的合约名,TestToken是我的sol文件名也是我要部署的合约名(class name)
  const artifactTestToken = await deployer.loadArtifact("TestToken");

  // 计算gas fee
  // 参数为合约中construct的参数, 这个就看具体合约怎么写的了
  // uint256 _initialAmount, string memory _tokenName, uint8 _decimalUnits, string memory _tokenSymbol
  const initialAmount = 10000000000000000; // 1亿币,8位有效数字
  const tokenName = "TestToken";
  const tokenDecimal = 8;
  const tokenSymbol = "TT";
  const deploymentFee = await deployer.estimateDeployFee(artifactTestToken, [initialAmount,tokenName,tokenDecimal,tokenSymbol]);

  // 部署合约之前,先查看eth需要消耗多少
  const parsedFee = ethers.utils.formatEther(deploymentFee.toString());
  console.log(`The deployment is estimated to cost ${parsedFee} ETH`);

  // 同步执行合约部署
  const testTokenContract = await deployer.deploy(artifactTestToken, [initialAmount,tokenName,tokenDecimal,tokenSymbol]);

  console.log("constructor args:" + testTokenContract.interface.encodeDeploy([initialAmount,tokenName,tokenDecimal,tokenSymbol]));

  // 获得合约部署后的合约地址,有些多个合约,可能会用到前面合约的地址,那么这里就可以拿到地址,下面可以继续部署其他合约
  const contractAddress = testTokenContract.address;
  console.log(`${artifactTestToken.contractName} was deployed to ${contractAddress}`);
}

执行编译,部署

编译:yarn hardhat compile
部署:yarn hardhat deploy-zksync

开源

开源稍微有点复杂,而且zkSync的开源连一个注释都不能错,即之前用什么sol文件编译部署的,那么开源也必须是那个sol文件,注释都不能改。 开源需要三个数据: 1. 部署后的合约地址, 很好获取 2. 部署的合约文件地址与合约名, 很好获取 3. 合约部署时提供的参数, 稍微有点复杂

合约部署时提供的参数说明
先在根目录创建文件夹args
mkdir args
创建参数文件,参数需要放在文件中
vi args/TestToken.js

args/TestToken.js文件说明

module.exports = [
  10000000000000000, // 第一个参数
  "TestToken", // 第二个参数
  8, // 第三个参数
  "TT", // 第四个参数
];

执行开源:

yarn hardhat verify --network zkSync 合约地址 --contract contracts/TestToken.sol:TestToken --constructor-args args/TestToken.js

zkSync总结

zkSync的合约编译与hardhat的正常使用大差不差,主要区别在于那个zksolc的下载,官网文档也没有写清楚,各种查资料才发现,自己下载编译好的zksolc然后设置路径是最方便的。

二、其他L2网络,Linea、Scroll与Taiko

表彰大会

这四个L2里面最赞的是Taiko,因为Taiko无缝对接了Remix,合约可以直接在Remix上不做任何修改与主网一样的方式进行部署。太赞了。

Linea与Scroll

这两个L2虽然也可以在Remix部署,但是需要做一点修改。

Linea与Scroll在Remix编译时,Evm Version均不能使用最新版本,必须改成paris,即shanghai升级的部分不支持。

三、值得一提的事情

我特意去本地部署了一下Linea的编译环境,Linea官网提供的所有部署方式我都试过了,最后用Docker+truffle的方式编译成功,而且耗时8小时,我就不明白了,搞个docker会死吗。项目方肯定是多条链部署,不同的链都有不同的依赖包,各种相互依赖,Docker才是正解。

有需要Linera Truffle编译Docker的可以留言,我可以考虑开放出来。