Add zksync deployment files (#450)
This commit is contained in:
parent
c92329c7c4
commit
3b5c159e0b
|
@ -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
|
|
@ -5,3 +5,5 @@ cache
|
|||
.openzeppelin
|
||||
*mnemonic*
|
||||
!devnet-mnemonic.txt
|
||||
cache-zk
|
||||
artifacts-zk
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
)
|
||||
);
|
||||
}
|
|
@ -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."
|
||||
);
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
{
|
||||
"contractName": "WormholeReceiver",
|
||||
"address": "0x02C404128Ba4b83f4Ea8c134Ca30A7Aa07aac535"
|
||||
},
|
||||
{
|
||||
"contractName": "PythUpgradable",
|
||||
"address": "0xF532F2C1bB7b67E08f7D8B76f9fF804D0831725e"
|
||||
}
|
||||
]
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -261,7 +261,7 @@ module.exports = {
|
|||
},
|
||||
network_id: 324,
|
||||
},
|
||||
zksync_testnet: {
|
||||
zksync_goerli: {
|
||||
provider: () => {
|
||||
return new HDWalletProvider(
|
||||
process.env.MNEMONIC,
|
||||
|
|
Loading…
Reference in New Issue