| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- import yargs from "yargs";
- import { hideBin } from "yargs/helpers";
- import { EvmChain } from "../src/chains";
- import { DefaultStore } from "../src/store";
- import { existsSync, readFileSync, writeFileSync } from "fs";
- import {
- DeploymentType,
- EvmPriceFeedContract,
- getDefaultDeploymentConfig,
- PrivateKey,
- toDeploymentType,
- toPrivateKey,
- WormholeEvmContract,
- } from "../src";
- import { join } from "path";
- import Web3 from "web3";
- import { Contract } from "web3-eth-contract";
- type DeploymentConfig = {
- type: DeploymentType;
- validTimePeriodSeconds: number;
- singleUpdateFeeInWei: number;
- gasMultiplier: number;
- gasPriceMultiplier: number;
- privateKey: PrivateKey;
- jsonOutputDir: string;
- saveContract: boolean;
- };
- const CACHE_FILE = ".cache-deploy-evm";
- const parser = yargs(hideBin(process.argv))
- .scriptName("deploy_evm_pricefeed_contracts.ts")
- .usage(
- "Usage: $0 --std-output-dir <path/to/std-output-dir/> --private-key <private-key> --chain <chain0> --chain <chain1>"
- )
- .options({
- "std-output-dir": {
- type: "string",
- demandOption: true,
- desc: "Path to the standard JSON output of the contracts (build artifact) directory",
- },
- "private-key": {
- type: "string",
- demandOption: true,
- desc: "Private key to use for the deployment",
- },
- chain: {
- type: "array",
- demandOption: true,
- desc: "Chain to upload the contract on. Can be one of the evm chains available in the store",
- },
- "deployment-type": {
- type: "string",
- demandOption: false,
- default: "stable",
- desc: "Deployment type to use. Can be 'stable' or 'beta'",
- },
- "valid-time-period-seconds": {
- type: "number",
- demandOption: false,
- default: 60,
- desc: "Valid time period in seconds for the price feed staleness",
- },
- "single-update-fee-in-wei": {
- type: "number",
- demandOption: false,
- default: 1,
- desc: "Single update fee in wei for the price feed",
- },
- "gas-multiplier": {
- type: "number",
- demandOption: false,
- // Pyth Proxy (ERC1967) gas estimate is insufficient in many networks and thus we use 2 by default to make it work.
- default: 2,
- desc: "Gas multiplier to use for the deployment. This is useful when gas estimates are not accurate",
- },
- "gas-price-multiplier": {
- type: "number",
- demandOption: false,
- default: 1,
- desc: "Gas price multiplier to use for the deployment. This is useful when gas price estimates are not accurate",
- },
- "save-contract": {
- type: "boolean",
- demandOption: false,
- default: true,
- desc: "Save the contract to the store",
- },
- });
- async function deployIfNotCached(
- chain: EvmChain,
- config: DeploymentConfig,
- artifactName: string,
- deployArgs: any[] // eslint-disable-line @typescript-eslint/no-explicit-any
- ): Promise<string> {
- const cache = existsSync(CACHE_FILE)
- ? JSON.parse(readFileSync(CACHE_FILE, "utf8"))
- : {};
- const cacheKey = `${chain.getId()}-${artifactName}`;
- if (cache[cacheKey]) {
- const address = cache[cacheKey];
- console.log(
- `Using cached deployment of ${artifactName} on ${chain.getId()} at ${address}`
- );
- return address;
- }
- const artifact = JSON.parse(
- readFileSync(join(config.jsonOutputDir, `${artifactName}.json`), "utf8")
- );
- console.log(`Deploying ${artifactName} on ${chain.getId()}...`);
- const addr = await chain.deploy(
- config.privateKey,
- artifact["abi"],
- artifact["bytecode"],
- deployArgs,
- config.gasMultiplier,
- config.gasPriceMultiplier
- );
- console.log(`✅ Deployed ${artifactName} on ${chain.getId()} at ${addr}`);
- cache[cacheKey] = addr;
- writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
- return addr;
- }
- function getWeb3Contract(
- config: DeploymentConfig,
- artifactName: string,
- address: string
- ): Contract {
- const artifact = JSON.parse(
- readFileSync(join(config.jsonOutputDir, `${artifactName}.json`), "utf8")
- );
- const web3 = new Web3();
- return new web3.eth.Contract(artifact["abi"], address);
- }
- async function deployWormholeReceiverContracts(
- chain: EvmChain,
- config: DeploymentConfig
- ): Promise<string> {
- const receiverSetupAddr = await deployIfNotCached(
- chain,
- config,
- "ReceiverSetup",
- []
- );
- const receiverImplAddr = await deployIfNotCached(
- chain,
- config,
- "ReceiverImplementation",
- []
- );
- // Craft the init data for the proxy contract
- const setupContract = getWeb3Contract(
- config,
- "ReceiverSetup",
- receiverSetupAddr
- );
- const { wormholeConfig } = getDefaultDeploymentConfig(config.type);
- const initData = setupContract.methods
- .setup(
- receiverImplAddr,
- wormholeConfig.initialGuardianSet.map((addr: string) => "0x" + addr),
- chain.getWormholeChainId(),
- wormholeConfig.governanceChainId,
- "0x" + wormholeConfig.governanceContract
- )
- .encodeABI();
- const wormholeReceiverAddr = await deployIfNotCached(
- chain,
- config,
- "WormholeReceiver",
- [receiverSetupAddr, initData]
- );
- const wormholeEvmContract = new WormholeEvmContract(
- chain,
- wormholeReceiverAddr
- );
- if (config.type === "stable") {
- console.log(`Syncing mainnet guardian sets for ${chain.getId()}...`);
- // TODO: Add a way to pass gas configs to this
- await wormholeEvmContract.syncMainnetGuardianSets(config.privateKey);
- console.log(`✅ Synced mainnet guardian sets for ${chain.getId()}`);
- }
- return wormholeReceiverAddr;
- }
- async function deployPriceFeedContracts(
- chain: EvmChain,
- config: DeploymentConfig,
- wormholeAddr: string
- ): Promise<string> {
- const pythImplAddr = await deployIfNotCached(
- chain,
- config,
- "PythUpgradable",
- []
- );
- // Craft the init data for the proxy contract
- const { dataSources, governanceDataSource } = getDefaultDeploymentConfig(
- config.type
- );
- const pythImplContract = getWeb3Contract(
- config,
- "PythUpgradable",
- pythImplAddr
- );
- const pythInitData = pythImplContract.methods
- .initialize(
- wormholeAddr,
- dataSources.map((ds) => ds.emitterChain),
- dataSources.map((ds) => "0x" + ds.emitterAddress),
- governanceDataSource.emitterChain,
- "0x" + governanceDataSource.emitterAddress,
- 0, // governanceInitialSequence
- config.validTimePeriodSeconds,
- config.singleUpdateFeeInWei
- )
- .encodeABI();
- return await deployIfNotCached(chain, config, "ERC1967Proxy", [
- pythImplAddr,
- pythInitData,
- ]);
- }
- async function main() {
- const argv = await parser.argv;
- const deploymentConfig: DeploymentConfig = {
- type: toDeploymentType(argv.deploymentType),
- validTimePeriodSeconds: argv.validTimePeriodSeconds,
- singleUpdateFeeInWei: argv.singleUpdateFeeInWei,
- gasMultiplier: argv.gasMultiplier,
- gasPriceMultiplier: argv.gasPriceMultiplier,
- privateKey: toPrivateKey(argv.privateKey),
- jsonOutputDir: argv.stdOutputDir,
- saveContract: argv.saveContract,
- };
- console.log(
- `Deployment config: ${JSON.stringify(deploymentConfig, null, 2)}\n`
- );
- const chainNames = argv.chain;
- for (const chainName of chainNames) {
- const chain = DefaultStore.chains[chainName];
- if (!chain) {
- throw new Error(`Chain ${chain} not found`);
- } else if (!(chain instanceof EvmChain)) {
- throw new Error(`Chain ${chain} is not an EVM chain`);
- }
- console.log(`Deploying price feed contracts on ${chain.getId()}...`);
- const wormholeAddr = await deployWormholeReceiverContracts(
- chain,
- deploymentConfig
- );
- const priceFeedAddr = await deployPriceFeedContracts(
- chain,
- deploymentConfig,
- wormholeAddr
- );
- if (deploymentConfig.saveContract) {
- console.log("Saving the contract in the store...");
- const contract = new EvmPriceFeedContract(chain, priceFeedAddr);
- DefaultStore.contracts[contract.getId()] = contract;
- DefaultStore.saveAllContracts();
- }
- console.log(
- `✅ Deployed price feed contracts on ${chain.getId()} at ${priceFeedAddr}\n\n`
- );
- }
- }
- main();
|