瀏覽代碼

Add zksync deployment files (#450)

Ali Behjati 2 年之前
父節點
當前提交
3b5c159e0b

+ 1 - 1
ethereum/.env.prod.zksync_testnet → ethereum/.env.prod.zksync_goerli

@@ -1,5 +1,5 @@
 MIGRATIONS_DIR=./migrations/prod-receiver
-MIGRATIONS_NETWORK=zksync_testnet
+MIGRATIONS_NETWORK=zksync_goerli
 WORMHOLE_CHAIN_NAME=zksync
 CLUSTER=testnet
 VALID_TIME_PERIOD_SECONDS=60

+ 2 - 0
ethereum/.gitignore

@@ -5,3 +5,5 @@ cache
 .openzeppelin
 *mnemonic*
 !devnet-mnemonic.txt
+cache-zk
+artifacts-zk

+ 16 - 0
ethereum/Deploying.md

@@ -82,6 +82,8 @@ This is the deployment process:
    need to approve the created transactions. Links to the multisig transactions are printed during the
    script execution and you can use them. You need to run the script when the transactions are approved.
    If the deployment script runs successfully you should see many ✅s and no ❌s with a successful message.
+   Please note that if you need to deploy/upgrade a zkSync network contract, you should deploy/upgrade it manually first
+   as described below.
 7. On first time deployments for a network with Wormhole Receiver contract, run this command:
    ```bash
    npm run receiver-submit-guardian-sets -- --network <network>
@@ -161,3 +163,17 @@ It will create a new file `PythUpgradable_merged.sol` which you can use in the e
   migration. However, if it happens, you can comment out the part that is already ran (you can double check in the explorer), and re-run the migration.
   You can avoid gas problems by choosing a much higher gas than what is showed on the network gas tracker. Also, you can find other rpc nodes from
   [here](https://chainlist.org/)
+
+# Deploy/Upgrade on zkSync networks
+
+Although zkSync is EVM compatible, their binary format is different than solc output. So, we need to use their libraries to
+compile it to their binary format (zk-solc) and deploy it. As of this writing they only support hardhat. To deploy a fresh
+contract or a new contract do the following steps in addition to the steps described above:
+
+1. Update the [`hardhad.config.ts`](./hardhat.config.ts) file.
+2. Add the configuration files to `truffle-config.js` and `.env.prod.<network>` file as described above. Truffle
+   config is required as the above deployment script still works in changing the contract (except upgrades).
+3. Run `npx hardhat compile` to compile the contracts.
+4. If you wish to deploy the contract run `npx hardhat deploy-zksync --script deploy/zkSyncDeploy` to deploy it to the new network. Otherwise
+   run `npx hardhat deploy-zksync --script deploy/zkSyncDeployNewPythImpl.ts` to get a new implementation address. Then put it in
+   `.<network>.new_impl` file and run the deployment script to handle the rest of the changes.

+ 7 - 4
ethereum/deploy.sh

@@ -31,10 +31,13 @@ while [[ $# -ne 0 ]]; do
     # If it is a new chain you are deploying to, create a new env file and commit it to the repo.
     rm -f .env; ln -s .env.prod.$NETWORK .env && set -o allexport && source .env set && set +o allexport
 
-    echo "Migrating..."
-    npx truffle migrate --network $MIGRATIONS_NETWORK
-
-    echo "Deployment to $NETWORK finished successfully"
+    if [[ $NETWORK == zksync* ]]; then
+        echo "Skipping truffle migration on $NETWORK. If you wish to deploy a fresh contract read Deploying.md."
+    else
+        echo "Migrating..."
+        npx truffle migrate --network $MIGRATIONS_NETWORK
+        echo "Deployment to $NETWORK finished successfully"
+    fi
 
     echo "=========== Syncing contract state ==========="
     npx truffle exec scripts/syncPythState.js --network $MIGRATIONS_NETWORK || echo "Syncing failed/incomplete.. skipping"

+ 147 - 0
ethereum/deploy/zkSyncDeploy.ts

@@ -0,0 +1,147 @@
+import { utils, Wallet } from "zksync-web3";
+import { HardhatRuntimeEnvironment } from "hardhat/types";
+import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
+import loadEnv from "../scripts/loadEnv";
+import { CHAINS } from "@pythnetwork/xc-governance-sdk";
+import { assert } from "chai";
+import { writeFileSync } from "fs";
+
+loadEnv("./");
+
+function envOrErr(name: string): string {
+  const res = process.env[name];
+  if (res === undefined) {
+    throw new Error(`${name} environment variable is not set.`);
+  }
+  return res;
+}
+
+export default async function (hre: HardhatRuntimeEnvironment) {
+  // Initialize the wallet.
+  const wallet = Wallet.fromMnemonic(envOrErr("MNEMONIC"));
+
+  // Create deployer object and load the artifact of the contract we want to deploy.
+  const deployer = new Deployer(hre, wallet);
+
+  // Deposit some funds to L2 in order to be able to perform L2 transactions. Uncomment
+  // this if the deployment account is unfunded.
+  //
+  // const depositAmount = ethers.utils.parseEther("0.005");
+  // const depositHandle = await deployer.zkWallet.deposit({
+  //   to: deployer.zkWallet.address,
+  //   token: utils.ETH_ADDRESS,
+  //   amount: depositAmount,
+  // });
+  // // Wait until the deposit is processed on zkSync
+  // await depositHandle.wait();
+
+  // Deploy WormholeReceiver contract.
+  const initialSigners = JSON.parse(envOrErr("INIT_SIGNERS"));
+  const whGovernanceChainId = envOrErr("INIT_GOV_CHAIN_ID");
+  const whGovernanceContract = envOrErr("INIT_GOV_CONTRACT"); // bytes32
+
+  const chainName = envOrErr("WORMHOLE_CHAIN_NAME");
+  const wormholeReceiverChainId = CHAINS[chainName];
+  assert(wormholeReceiverChainId !== undefined);
+
+  const receiverSetupArtifact = await deployer.loadArtifact("ReceiverSetup");
+  const receiverImplArtifact = await deployer.loadArtifact(
+    "ReceiverImplementation"
+  );
+  const wormholeReceiverArtifact = await deployer.loadArtifact(
+    "WormholeReceiver"
+  );
+
+  const receiverSetupContract = await deployer.deploy(receiverSetupArtifact);
+
+  // deploy implementation
+  const receiverImplContract = await deployer.deploy(receiverImplArtifact);
+
+  // encode initialisation data
+  const whInitData = receiverSetupContract.interface.encodeFunctionData(
+    "setup",
+    [
+      receiverImplContract.address,
+      initialSigners,
+      wormholeReceiverChainId,
+      whGovernanceChainId,
+      whGovernanceContract,
+    ]
+  );
+
+  // deploy proxy
+  const wormholeReceiverContract = await deployer.deploy(
+    wormholeReceiverArtifact,
+    [receiverSetupContract.address, whInitData]
+  );
+
+  console.log(
+    `Deployed WormholeReceiver on ${wormholeReceiverContract.address}`
+  );
+
+  // Deploy Pyth contract.
+  const emitterChainIds = [
+    envOrErr("SOLANA_CHAIN_ID"),
+    envOrErr("PYTHNET_CHAIN_ID"),
+  ];
+  const emitterAddresses = [
+    envOrErr("SOLANA_EMITTER"),
+    envOrErr("PYTHNET_EMITTER"),
+  ];
+  const governanceChainId = envOrErr("GOVERNANCE_CHAIN_ID");
+  const governanceEmitter = envOrErr("GOVERNANCE_EMITTER");
+  // Default value for this field is 0
+  const governanceInitialSequence = Number(
+    process.env.GOVERNANCE_INITIAL_SEQUENCE ?? "0"
+  );
+
+  const validTimePeriodSeconds = Number(envOrErr("VALID_TIME_PERIOD_SECONDS"));
+  const singleUpdateFeeInWei = Number(envOrErr("SINGLE_UPDATE_FEE_IN_WEI"));
+
+  const pythImplArtifact = await deployer.loadArtifact("PythUpgradable");
+  const pythProxyArtifact = await deployer.loadArtifact("ERC1967Proxy");
+
+  const pythImplContract = await deployer.deploy(pythImplArtifact);
+
+  const pythInitData = pythImplContract.interface.encodeFunctionData(
+    "initialize",
+    [
+      wormholeReceiverContract.address,
+      emitterChainIds,
+      emitterAddresses,
+      governanceChainId,
+      governanceEmitter,
+      governanceInitialSequence,
+      validTimePeriodSeconds,
+      singleUpdateFeeInWei,
+    ]
+  );
+
+  const pythProxyContract = await deployer.deploy(pythProxyArtifact, [
+    pythImplContract.address,
+    pythInitData,
+  ]);
+
+  console.log(`Deployed Pyth contract on ${pythProxyContract.address}`);
+
+  const networkId = hre.network.config.chainId;
+  const registryPath = `networks/${networkId}.json`;
+  console.log(`Saving addresses in ${registryPath}`);
+  writeFileSync(
+    registryPath,
+    JSON.stringify(
+      [
+        {
+          contractName: "WormholeReceiver",
+          address: wormholeReceiverContract.address,
+        },
+        {
+          contractName: "PythUpgradable",
+          address: pythProxyContract.address,
+        },
+      ],
+      null,
+      2
+    )
+  );
+}

+ 48 - 0
ethereum/deploy/zkSyncDeployNewPythImpl.ts

@@ -0,0 +1,48 @@
+import { utils, Wallet } from "zksync-web3";
+import { HardhatRuntimeEnvironment } from "hardhat/types";
+import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
+import loadEnv from "../scripts/loadEnv";
+import { CHAINS } from "@pythnetwork/xc-governance-sdk";
+import { assert } from "chai";
+import { writeFileSync } from "fs";
+import { ethers } from "ethers";
+
+loadEnv("./");
+
+function envOrErr(name: string): string {
+  const res = process.env[name];
+  if (res === undefined) {
+    throw new Error(`${name} environment variable is not set.`);
+  }
+  return res;
+}
+
+export default async function (hre: HardhatRuntimeEnvironment) {
+  // Initialize the wallet.
+  const wallet = Wallet.fromMnemonic(envOrErr("MNEMONIC"));
+
+  // Create deployer object and load the artifact of the contract we want to deploy.
+  const deployer = new Deployer(hre, wallet);
+
+  // Deposit some funds to L2 in order to be able to perform L2 transactions. Uncomment
+  // this if the deployment account is unfunded.
+  //
+  // const depositAmount = ethers.utils.parseEther("0.005");
+  // const depositHandle = await deployer.zkWallet.deposit({
+  //   to: deployer.zkWallet.address,
+  //   token: utils.ETH_ADDRESS,
+  //   amount: depositAmount,
+  // });
+  // // Wait until the deposit is processed on zkSync
+  // await depositHandle.wait();
+
+  const pythImplArtifact = await deployer.loadArtifact("PythUpgradable");
+  const pythImplContract = await deployer.deploy(pythImplArtifact);
+
+  console.log(
+    `Deployed Pyth implementation contract on ${pythImplContract.address}`
+  );
+  console.log(
+    "Please use this address as the candidate new implementation in the deployment script."
+  );
+}

+ 32 - 0
ethereum/hardhat.config.ts

@@ -0,0 +1,32 @@
+require("@matterlabs/hardhat-zksync-deploy");
+require("@matterlabs/hardhat-zksync-solc");
+
+module.exports = {
+  zksolc: {
+    version: "1.2.0",
+    compilerSource: "binary",
+    settings: {
+      optimizer: {
+        enabled: true,
+      },
+    },
+  },
+  defaultNetwork: "zkTestnet",
+  networks: {
+    zkTestnet: {
+      url: "https://zksync2-testnet.zksync.dev", // URL of the zkSync network RPC
+      ethNetwork: "goerli", // Can also be the RPC URL of the Ethereum network (e.g. `https://goerli.infura.io/v3/<API_KEY>`)
+      zksync: true,
+      chainId: 280,
+    },
+  },
+  solidity: {
+    version: "0.8.4",
+    settings: {
+      optimizer: {
+        enabled: true,
+        runs: 10000,
+      },
+    },
+  },
+};

+ 10 - 0
ethereum/networks/280.json

@@ -0,0 +1,10 @@
+[
+  {
+    "contractName": "WormholeReceiver",
+    "address": "0x02C404128Ba4b83f4Ea8c134Ca30A7Aa07aac535"
+  },
+  {
+    "contractName": "PythUpgradable",
+    "address": "0xF532F2C1bB7b67E08f7D8B76f9fF804D0831725e"
+  }
+]

File diff suppressed because it is too large
+ 227 - 538
ethereum/package-lock.json


+ 8 - 2
ethereum/package.json

@@ -30,20 +30,26 @@
   "dependencies": {
     "@certusone/wormhole-sdk": "^0.8.0",
     "@certusone/wormhole-sdk-wasm": "^0.0.1",
+    "@matterlabs/hardhat-zksync-deploy": "^0.6.1",
+    "@matterlabs/hardhat-zksync-solc": "^0.3.13",
     "@openzeppelin/contracts": "^4.5.0",
     "@openzeppelin/contracts-upgradeable": "^4.5.2",
     "@pythnetwork/pyth-sdk-solidity": "^2.2.0",
     "@pythnetwork/xc-governance-sdk": "file:../third_party/pyth/xc-governance-sdk-js",
     "dotenv": "^10.0.0",
     "elliptic": "^6.5.2",
-    "ethers": "^5.6.8",
+    "ethers": "^5.7.2",
     "ganache-cli": "^6.12.1",
+    "hardhat": "^2.12.5",
     "jsonfile": "^4.0.0",
     "lodash": "^4.17.21",
     "solc": "^0.8.4",
     "truffle-contract-size": "^2.0.1",
+    "ts-node": "^10.9.1",
+    "typescript": "^4.9.4",
     "web3": "^1.2.2",
     "web3-eth-abi": "^1.2.2",
-    "web3-utils": "^1.2.2"
+    "web3-utils": "^1.2.2",
+    "zksync-web3": "^0.12.3"
   }
 }

+ 12 - 1
ethereum/scripts/syncPythState.js

@@ -215,7 +215,18 @@ async function upgradeContract(proxy, desiredVersion) {
     );
   } else {
     console.log("Deploying a new implementation...");
-    const newImplementation = await PythUpgradable.new();
+
+    let newImplementation;
+    try {
+      newImplementation = await PythUpgradable.new();
+    } catch (e) {
+      console.error(
+        "Could not deploy the new contract. Please try again. If this is a zkSync " +
+          "network truffle cannot it and you need to deploy it manually (as described in Deploying.md) "`and store the address on ${implCachePath} file. Then rerun the script.`
+      );
+      throw e;
+    }
+
     console.log(`Tx hash:  ${newImplementation.transactionHash}`);
     console.log(`New implementation address: ${newImplementation.address}`);
     fs.writeFileSync(implCachePath, newImplementation.address);

+ 1 - 1
ethereum/truffle-config.js

@@ -261,7 +261,7 @@ module.exports = {
       },
       network_id: 324,
     },
-    zksync_testnet: {
+    zksync_goerli: {
       provider: () => {
         return new HDWalletProvider(
           process.env.MNEMONIC,

Some files were not shown because too many files changed in this diff